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