Reduce showmount timeouts.
[nfs-utils.git] / utils / showmount / showmount.c
1 /*
2  * showmount.c -- show mount information for an NFS server
3  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18 #endif
19
20 #include <stdio.h>
21 #include <rpc/rpc.h>
22 #include <rpc/pmap_prot.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <sys/time.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <memory.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32
33 #include <netdb.h>
34 #include <arpa/inet.h>
35 #include <errno.h>
36 #include <getopt.h>
37 #include <mount.h>
38 #include <unistd.h>
39
40 #define TIMEOUT_UDP     3
41 #define TIMEOUT_TCP     10
42 #define TOTAL_TIMEOUT   20
43
44 static char *   version = "showmount for " VERSION;
45 static char *   program_name;
46 static int      headers = 1;
47 static int      hflag = 0;
48 static int      aflag = 0;
49 static int      dflag = 0;
50 static int      eflag = 0;
51
52 static struct option longopts[] =
53 {
54         { "all", 0, 0, 'a' },
55         { "directories", 0, 0, 'd' },
56         { "exports", 0, 0, 'e' },
57         { "no-headers", 0, &headers, 0 },
58         { "version", 0, 0, 'v' },
59         { "help", 0, 0, 'h' },
60         { NULL, 0, 0, 0 }
61 };
62
63 #define MAXHOSTLEN 256
64
65 int dump_cmp(p, q)
66 char **p;
67 char **q;
68 {
69         return strcmp(*p, *q);
70 }
71
72 static void usage(fp, n)
73 FILE *fp;
74 int n;
75 {
76         fprintf(fp, "Usage: %s [-adehv]\n", program_name);
77         fprintf(fp, "       [--all] [--directories] [--exports]\n");
78         fprintf(fp, "       [--no-headers] [--help] [--version] [host]\n");
79         exit(n);
80 }
81
82 /*
83  *  Perform a non-blocking connect on the socket fd.
84  *
85  *  tout contains the timeout.  It will be modified to contain the time
86  *  remaining (i.e. time provided - time elasped).
87  */
88 static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout)
89 {
90         int flags, ret;
91         socklen_t len;
92         fd_set rset;
93
94         flags = fcntl(fd, F_GETFL, 0);
95         if (flags < 0)
96                 return -1;
97
98         ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
99         if (ret < 0)
100                 return -1;
101
102         /*
103          * From here on subsequent sys calls could change errno so
104          * we set ret = -errno to capture it in case we decide to
105          * use it later.
106          */
107         len = sizeof(struct sockaddr);
108         ret = connect(fd, (struct sockaddr *)addr, len);
109         if (ret < 0 && errno != EINPROGRESS) {
110                 ret = -errno;
111                 goto done;
112         }
113
114         if (ret == 0)
115                 goto done;
116
117         /* now wait */
118         FD_ZERO(&rset);
119         FD_SET(fd, &rset);
120
121         ret = select(fd + 1, &rset, NULL, NULL, tout);
122         if (ret <= 0) {
123                 if (ret == 0)
124                         ret = -ETIMEDOUT;
125                 else
126                         ret = -errno;
127                 goto done;
128         }
129
130         if (FD_ISSET(fd, &rset)) {
131                 int status;
132
133                 len = sizeof(ret);
134                 status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
135                 if (status < 0) {
136                         ret = -errno;
137                         goto done;
138                 }
139
140                 /* Oops - something wrong with connect */
141                 if (ret)
142                         ret = -ret;
143         }
144
145 done:
146         fcntl(fd, F_SETFL, flags);
147         return ret;
148 }
149
150 static unsigned short getport(struct sockaddr_in *addr,
151                          unsigned long prog, unsigned long vers, int prot)
152 {
153         CLIENT *client;
154         enum clnt_stat status;
155         struct pmap parms;
156         int ret, sock;
157         struct sockaddr_in laddr, saddr;
158         struct timeval tout = {0, 0};
159         socklen_t len;
160         unsigned int send_sz = 0;
161         unsigned int recv_sz = 0;
162         unsigned short port;
163
164         memset(&laddr, 0, sizeof(laddr));
165         memset(&saddr, 0, sizeof(saddr));
166         memset(&parms, 0, sizeof(parms));
167
168         memcpy(&saddr, addr, sizeof(saddr));
169         saddr.sin_port = htons(PMAPPORT);
170
171         if (prot == IPPROTO_TCP) {
172                 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
173                 if (sock == -1) {
174                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
175                         rpc_createerr.cf_error.re_errno = errno;
176                         return 0;
177                 }
178
179                 tout.tv_sec = TIMEOUT_TCP;
180
181                 ret = connect_nb(sock, &saddr, &tout);
182                 if (ret == -1) {
183                         close(sock);
184                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
185                         rpc_createerr.cf_error.re_errno = errno;
186                         return 0;
187                 }
188         } else {
189                 /*
190                  * bind to any unused port.  If we left this up to the rpc
191                  * layer, it would bind to a reserved port, which has been shown
192                  * to exhaust the reserved port range in some situations.
193                  */
194                 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
195                 if (sock == -1) {
196                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
197                         rpc_createerr.cf_error.re_errno = errno;
198                         return 0;
199                 }
200
201                 laddr.sin_family = AF_INET;
202                 laddr.sin_port = 0;
203                 laddr.sin_addr.s_addr = htonl(INADDR_ANY);
204
205                 tout.tv_sec = TIMEOUT_UDP;
206
207                 send_sz = RPCSMALLMSGSIZE;
208                 recv_sz = RPCSMALLMSGSIZE;
209
210                 len = sizeof(struct sockaddr_in);
211                 if (bind(sock, (struct sockaddr *)&laddr, len) < 0) {
212                         close(sock);
213                         sock = RPC_ANYSOCK;
214                         /* FALLTHROUGH */
215                 }
216         }
217
218         client = clntudp_bufcreate(&saddr, PMAPPROG, PMAPVERS,
219                                    tout, &sock, send_sz, recv_sz);
220         if (!client) {
221                 close(sock);
222                 rpc_createerr.cf_stat = RPC_RPCBFAILURE;
223                 return 0;
224         }
225
226         clnt_control(client, CLSET_FD_CLOSE, NULL);
227
228         parms.pm_prog = prog;
229         parms.pm_vers = vers;
230         parms.pm_prot = prot;
231
232         status = clnt_call(client, PMAPPROC_GETPORT,
233                            (xdrproc_t) xdr_pmap, (caddr_t) &parms,
234                            (xdrproc_t) xdr_u_short, (caddr_t) &port,
235                            tout);
236
237         if (status != RPC_SUCCESS) {
238                 clnt_geterr(client, &rpc_createerr.cf_error);
239                 rpc_createerr.cf_stat = status;
240                 clnt_destroy(client);
241                 return 0;
242         }
243
244         clnt_destroy(client);
245
246         return htons(port);
247 }
248
249 int main(argc, argv)
250 int argc;
251 char **argv;
252 {
253         char hostname_buf[MAXHOSTLEN];
254         char *hostname;
255         enum clnt_stat clnt_stat;
256         struct hostent *hp;
257         struct sockaddr_in server_addr;
258         int ret, msock;
259         struct timeval total_timeout;
260         struct timeval pertry_timeout;
261         int c;
262         CLIENT *mclient;
263         groups grouplist;
264         exports exportlist, exl;
265         mountlist dumplist;
266         mountlist list;
267         int i;
268         int n;
269         int maxlen;
270         char **dumpv;
271
272         program_name = argv[0];
273         while ((c = getopt_long(argc, argv, "adehv", longopts, NULL)) != EOF) {
274                 switch (c) {
275                 case 'a':
276                         aflag = 1;
277                         break;
278                 case 'd':
279                         dflag = 1;
280                         break;
281                 case 'e':
282                         eflag = 1;
283                         break;
284                 case 'h':
285                         usage(stdout, 0);
286                         break;
287                 case 'v':
288                         printf("%s\n", version);
289                         exit(0);
290                 case 0:
291                         break;
292                 case '?':
293                 default:
294                         usage(stderr, 1);
295                         break;
296                 }
297         }
298         argc -= optind;
299         argv += optind;
300
301         switch (aflag + dflag + eflag) {
302         case 0:
303                 hflag = 1;
304                 break;
305         case 1:
306                 break;
307         default:
308                 fprintf(stderr, "%s: only one of -a, -d or -e is allowed\n",
309                         program_name);
310                 exit(1);
311                 break;
312         }
313
314         switch (argc) {
315         case 0:
316                 if (gethostname(hostname_buf, MAXHOSTLEN) < 0) {
317                         perror("getting hostname");
318                         exit(1);
319                 }
320                 hostname = hostname_buf;
321                 break;
322         case 1:
323                 hostname = argv[0];
324                 break;
325         default:
326                 fprintf(stderr, "%s: only one hostname is allowed\n",
327                         program_name);
328                 exit(1);
329                 break;
330         }
331
332         if (inet_aton(hostname, &server_addr.sin_addr)) {
333                 server_addr.sin_family = AF_INET;
334         }
335         else {
336                 if ((hp = gethostbyname(hostname)) == NULL) {
337                         fprintf(stderr, "%s: can't get address for %s\n",
338                                 program_name, hostname);
339                         exit(1);
340                 }
341                 server_addr.sin_family = AF_INET;
342                 memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
343         }
344
345         /* create mount deamon client */
346
347         mclient = NULL;
348         msock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
349         if (msock != -1) {
350                 server_addr.sin_port = getport(&server_addr,
351                                          MOUNTPROG, MOUNTVERS, IPPROTO_TCP);
352                 if (server_addr.sin_port) {
353                         ret = connect_nb(msock, &server_addr, 0);
354                         if (ret != -1)
355                                 mclient = clnttcp_create(&server_addr,
356                                                 MOUNTPROG, MOUNTVERS, &msock,
357                                                 0, 0);
358                         else
359                                 close(msock);
360                 } else
361                         close(msock);
362         }
363
364         if (!mclient) {
365                 server_addr.sin_port = getport(&server_addr,
366                                          MOUNTPROG, MOUNTVERS, IPPROTO_UDP);
367                 if (!server_addr.sin_port) {
368                         clnt_pcreateerror("portmap getport");
369                         exit(1);
370                 }
371                 msock = RPC_ANYSOCK;
372                 pertry_timeout.tv_sec = TIMEOUT_UDP;
373                 pertry_timeout.tv_usec = 0;
374                 if ((mclient = clntudp_create(&server_addr,
375                     MOUNTPROG, MOUNTVERS, pertry_timeout, &msock)) == NULL) {
376                         clnt_pcreateerror("mount clntudp_create");
377                         exit(1);
378                 }
379         }
380         mclient->cl_auth = authunix_create_default();
381         total_timeout.tv_sec = TOTAL_TIMEOUT;
382         total_timeout.tv_usec = 0;
383
384         if (eflag) {
385                 memset(&exportlist, '\0', sizeof(exportlist));
386
387                 clnt_stat = clnt_call(mclient, MOUNTPROC_EXPORT,
388                         (xdrproc_t) xdr_void, NULL,
389                         (xdrproc_t) xdr_exports, (caddr_t) &exportlist,
390                         total_timeout);
391                 if (clnt_stat != RPC_SUCCESS) {
392                         clnt_perror(mclient, "rpc mount export");
393                         exit(1);
394                 }
395                 if (headers)
396                         printf("Export list for %s:\n", hostname);
397                 maxlen = 0;
398                 for (exl = exportlist; exl; exl = exl->ex_next) {
399                         if ((n = strlen(exl->ex_dir)) > maxlen)
400                                 maxlen = n;
401                 }
402                 while (exportlist) {
403                         printf("%-*s ", maxlen, exportlist->ex_dir);
404                         grouplist = exportlist->ex_groups;
405                         if (grouplist)
406                                 while (grouplist) {
407                                         printf("%s%s", grouplist->gr_name,
408                                                 grouplist->gr_next ? "," : "");
409                                         grouplist = grouplist->gr_next;
410                                 }
411                         else
412                                 printf("(everyone)");
413                         printf("\n");
414                         exportlist = exportlist->ex_next;
415                 }
416                 exit(0);
417         }
418
419         memset(&dumplist, '\0', sizeof(dumplist));
420         clnt_stat = clnt_call(mclient, MOUNTPROC_DUMP,
421                 (xdrproc_t) xdr_void, NULL,
422                 (xdrproc_t) xdr_mountlist, (caddr_t) &dumplist,
423                 total_timeout);
424         if (clnt_stat != RPC_SUCCESS) {
425                 clnt_perror(mclient, "rpc mount dump");
426                 exit(1);
427         }
428
429         n = 0;
430         for (list = dumplist; list; list = list->ml_next)
431                 n++;
432         dumpv = (char **) calloc(n, sizeof (char *));
433         if (n && !dumpv) {
434                 fprintf(stderr, "%s: out of memory\n", program_name);
435                 exit(1);
436         }
437         i = 0;
438
439         if (hflag) {
440                 if (headers)
441                         printf("Hosts on %s:\n", hostname);
442                 while (dumplist) {
443                         dumpv[i++] = dumplist->ml_hostname;
444                         dumplist = dumplist->ml_next;
445                 }
446         }
447         else if (aflag) {
448                 if (headers)
449                         printf("All mount points on %s:\n", hostname);
450                 while (dumplist) {
451                         char *t;
452
453                         t=malloc(strlen(dumplist->ml_hostname)+strlen(dumplist->ml_directory)+2);
454                         if (!t)
455                         {
456                                 fprintf(stderr, "%s: out of memory\n", program_name);
457                                 exit(1);
458                         }
459                         sprintf(t, "%s:%s", dumplist->ml_hostname, dumplist->ml_directory);
460                         dumpv[i++] = t;
461                         dumplist = dumplist->ml_next;
462                 }
463         }
464         else if (dflag) {
465                 if (headers)
466                         printf("Directories on %s:\n", hostname);
467                 while (dumplist) {
468                         dumpv[i++] = dumplist->ml_directory;
469                         dumplist = dumplist->ml_next;
470                 }
471         }
472
473         qsort(dumpv, n, sizeof (char *), dump_cmp);
474         
475         for (i = 0; i < n; i++) {
476                 if (i == 0 || strcmp(dumpv[i], dumpv[i - 1]) != 0)
477                         printf("%s\n", dumpv[i]);
478         }
479         exit(0);
480 }
481