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