]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/showmount/showmount.c
showmount command: support querying IPv6 servers
[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 TOTAL_TIMEOUT   20
44
45 static char *   version = "showmount for " VERSION;
46 static char *   program_name;
47 static int      headers = 1;
48 static int      hflag = 0;
49 static int      aflag = 0;
50 static int      dflag = 0;
51 static int      eflag = 0;
52
53 static const char *nfs_sm_pgmtbl[] = {
54         "showmount",
55         "mount",
56         "mountd",
57         NULL,
58 };
59
60 static struct option longopts[] =
61 {
62         { "all", 0, 0, 'a' },
63         { "directories", 0, 0, 'd' },
64         { "exports", 0, 0, 'e' },
65         { "no-headers", 0, &headers, 0 },
66         { "version", 0, 0, 'v' },
67         { "help", 0, 0, 'h' },
68         { NULL, 0, 0, 0 }
69 };
70
71 #define MAXHOSTLEN 256
72
73 static int dump_cmp(const void *pv, const void *qv)
74 {
75         const char **p = (const char **)pv;
76         const char **q = (const char **)qv;
77         return strcmp(*p, *q);
78 }
79
80 static void usage(FILE *fp, int n)
81 {
82         fprintf(fp, "Usage: %s [-adehv]\n", program_name);
83         fprintf(fp, "       [--all] [--directories] [--exports]\n");
84         fprintf(fp, "       [--no-headers] [--help] [--version] [host]\n");
85         exit(n);
86 }
87
88 #ifdef HAVE_CLNT_CREATE
89
90 /*
91  * Generate an RPC client handle connected to the mountd service
92  * at @hostname, or die trying.
93  *
94  * Supports both AF_INET and AF_INET6 server addresses.
95  */
96 static CLIENT *nfs_get_mount_client(const char *hostname)
97 {
98         rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, nfs_sm_pgmtbl);
99         CLIENT *client;
100
101         client = clnt_create(hostname, program, MOUNTVERS, "tcp");
102         if (client)
103                 return client;
104
105         client = clnt_create(hostname, program, MOUNTVERS, "udp");
106         if (client)
107                 return client;
108
109         clnt_pcreateerror("clnt_create");
110         exit(1);
111 }
112
113 #else   /* HAVE_CLNT_CREATE */
114
115 /*
116  *  Perform a non-blocking connect on the socket fd.
117  *
118  *  tout contains the timeout.  It will be modified to contain the time
119  *  remaining (i.e. time provided - time elasped).
120  *
121  *  Returns zero on success; otherwise, -1 is returned and errno is set
122  *  to reflect the nature of the error.
123  */
124 static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout)
125 {
126         int flags, ret;
127         socklen_t len;
128         fd_set rset;
129
130         flags = fcntl(fd, F_GETFL, 0);
131         if (flags < 0)
132                 return -1;
133
134         ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
135         if (ret < 0)
136                 return -1;
137
138         /*
139          * From here on subsequent sys calls could change errno so
140          * we set ret = -errno to capture it in case we decide to
141          * use it later.
142          */
143         len = sizeof(struct sockaddr);
144         ret = connect(fd, (struct sockaddr *)addr, len);
145         if (ret < 0 && errno != EINPROGRESS) {
146                 ret = -1;
147                 goto done;
148         }
149
150         if (ret == 0)
151                 goto done;
152
153         /* now wait */
154         FD_ZERO(&rset);
155         FD_SET(fd, &rset);
156
157         ret = select(fd + 1, NULL, &rset, NULL, tout);
158         if (ret <= 0) {
159                 if (ret == 0)
160                         errno = ETIMEDOUT;
161                 ret = -1;
162                 goto done;
163         }
164
165         if (FD_ISSET(fd, &rset)) {
166                 int status;
167
168                 len = sizeof(ret);
169                 status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
170                 if (status < 0) {
171                         ret = -1;
172                         goto done;
173                 }
174
175                 /* Oops - something wrong with connect */
176                 if (ret != 0) {
177                         errno = ret;
178                         ret = -1;
179                 }
180         }
181
182 done:
183         fcntl(fd, F_SETFL, flags);
184         return ret;
185 }
186
187 /*
188  * Generate an RPC client handle connected to the mountd service
189  * at @hostname, or die trying.
190  *
191  * Supports only AF_INET server addresses.
192  */
193 static CLIENT *nfs_get_mount_client(const char *hostname)
194 {
195         struct hostent *hp;
196         struct sockaddr_in server_addr;
197         struct timeval pertry_timeout;
198         CLIENT *mclient = NULL;
199         int ret, msock;
200
201         if (inet_aton(hostname, &server_addr.sin_addr)) {
202                 server_addr.sin_family = AF_INET;
203         }
204         else {
205                 if ((hp = gethostbyname(hostname)) == NULL) {
206                         fprintf(stderr, "%s: can't get address for %s\n",
207                                 program_name, hostname);
208                         exit(1);
209                 }
210                 server_addr.sin_family = AF_INET;
211                 memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
212         }
213
214         msock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
215         if (msock != -1) {
216                 if (nfs_getport_ping((struct sockaddr *)&server_addr,
217                                         sizeof(server_addr), MOUNTPROG,
218                                         MOUNTVERS, IPPROTO_TCP)) {
219                         ret = connect_nb(msock, &server_addr, 0);
220                         if (ret == 0)
221                                 mclient = clnttcp_create(&server_addr,
222                                                 MOUNTPROG, MOUNTVERS, &msock,
223                                                 0, 0);
224                         else
225                                 close(msock);
226                 } else
227                         close(msock);
228         }
229
230         if (!mclient) {
231                 if (nfs_getport_ping((struct sockaddr *)&server_addr,
232                                         sizeof(server_addr), MOUNTPROG,
233                                         MOUNTVERS, IPPROTO_UDP)) {
234                         clnt_pcreateerror("showmount");
235                         exit(1);
236                 }
237                 msock = RPC_ANYSOCK;
238                 pertry_timeout.tv_sec = TIMEOUT_UDP;
239                 pertry_timeout.tv_usec = 0;
240                 if ((mclient = clntudp_create(&server_addr,
241                     MOUNTPROG, MOUNTVERS, pertry_timeout, &msock)) == NULL) {
242                         clnt_pcreateerror("mount clntudp_create");
243                         exit(1);
244                 }
245         }
246
247         return mclient;
248 }
249
250 #endif  /* HAVE_CLNT_CREATE */
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 timeval total_timeout;
258         int c;
259         CLIENT *mclient;
260         groups grouplist;
261         exports exportlist, exl;
262         mountlist dumplist;
263         mountlist list;
264         int i;
265         int n;
266         int maxlen;
267         char **dumpv;
268
269         program_name = argv[0];
270         while ((c = getopt_long(argc, argv, "adehv", longopts, NULL)) != EOF) {
271                 switch (c) {
272                 case 'a':
273                         aflag = 1;
274                         break;
275                 case 'd':
276                         dflag = 1;
277                         break;
278                 case 'e':
279                         eflag = 1;
280                         break;
281                 case 'h':
282                         usage(stdout, 0);
283                         break;
284                 case 'v':
285                         printf("%s\n", version);
286                         exit(0);
287                 case 0:
288                         break;
289                 case '?':
290                 default:
291                         usage(stderr, 1);
292                         break;
293                 }
294         }
295         argc -= optind;
296         argv += optind;
297
298         switch (aflag + dflag + eflag) {
299         case 0:
300                 hflag = 1;
301                 break;
302         case 1:
303                 break;
304         default:
305                 fprintf(stderr, "%s: only one of -a, -d or -e is allowed\n",
306                         program_name);
307                 exit(1);
308                 break;
309         }
310
311         switch (argc) {
312         case 0:
313                 if (gethostname(hostname_buf, MAXHOSTLEN) < 0) {
314                         perror("getting hostname");
315                         exit(1);
316                 }
317                 hostname = hostname_buf;
318                 break;
319         case 1:
320                 hostname = argv[0];
321                 break;
322         default:
323                 fprintf(stderr, "%s: only one hostname is allowed\n",
324                         program_name);
325                 exit(1);
326                 break;
327         }
328
329         mclient = nfs_get_mount_client(hostname);
330         mclient->cl_auth = authunix_create_default();
331         total_timeout.tv_sec = TOTAL_TIMEOUT;
332         total_timeout.tv_usec = 0;
333
334         if (eflag) {
335                 memset(&exportlist, '\0', sizeof(exportlist));
336
337                 clnt_stat = clnt_call(mclient, MOUNTPROC_EXPORT,
338                         (xdrproc_t) xdr_void, NULL,
339                         (xdrproc_t) xdr_exports, (caddr_t) &exportlist,
340                         total_timeout);
341                 if (clnt_stat != RPC_SUCCESS) {
342                         clnt_perror(mclient, "rpc mount export");
343                         clnt_destroy(mclient);
344                         exit(1);
345                 }
346                 if (headers)
347                         printf("Export list for %s:\n", hostname);
348                 maxlen = 0;
349                 for (exl = exportlist; exl; exl = exl->ex_next) {
350                         if ((n = strlen(exl->ex_dir)) > maxlen)
351                                 maxlen = n;
352                 }
353                 while (exportlist) {
354                         printf("%-*s ", maxlen, exportlist->ex_dir);
355                         grouplist = exportlist->ex_groups;
356                         if (grouplist)
357                                 while (grouplist) {
358                                         printf("%s%s", grouplist->gr_name,
359                                                 grouplist->gr_next ? "," : "");
360                                         grouplist = grouplist->gr_next;
361                                 }
362                         else
363                                 printf("(everyone)");
364                         printf("\n");
365                         exportlist = exportlist->ex_next;
366                 }
367                 clnt_destroy(mclient);
368                 exit(0);
369         }
370
371         memset(&dumplist, '\0', sizeof(dumplist));
372         clnt_stat = clnt_call(mclient, MOUNTPROC_DUMP,
373                 (xdrproc_t) xdr_void, NULL,
374                 (xdrproc_t) xdr_mountlist, (caddr_t) &dumplist,
375                 total_timeout);
376         if (clnt_stat != RPC_SUCCESS) {
377                 clnt_perror(mclient, "rpc mount dump");
378                 clnt_destroy(mclient);
379                 exit(1);
380         }
381         clnt_destroy(mclient);
382
383         n = 0;
384         for (list = dumplist; list; list = list->ml_next)
385                 n++;
386         dumpv = (char **) calloc(n, sizeof (char *));
387         if (n && !dumpv) {
388                 fprintf(stderr, "%s: out of memory\n", program_name);
389                 exit(1);
390         }
391         i = 0;
392
393         if (hflag) {
394                 if (headers)
395                         printf("Hosts on %s:\n", hostname);
396                 while (dumplist) {
397                         dumpv[i++] = dumplist->ml_hostname;
398                         dumplist = dumplist->ml_next;
399                 }
400         }
401         else if (aflag) {
402                 if (headers)
403                         printf("All mount points on %s:\n", hostname);
404                 while (dumplist) {
405                         char *t;
406
407                         t=malloc(strlen(dumplist->ml_hostname)+strlen(dumplist->ml_directory)+2);
408                         if (!t)
409                         {
410                                 fprintf(stderr, "%s: out of memory\n", program_name);
411                                 exit(1);
412                         }
413                         sprintf(t, "%s:%s", dumplist->ml_hostname, dumplist->ml_directory);
414                         dumpv[i++] = t;
415                         dumplist = dumplist->ml_next;
416                 }
417         }
418         else if (dflag) {
419                 if (headers)
420                         printf("Directories on %s:\n", hostname);
421                 while (dumplist) {
422                         dumpv[i++] = dumplist->ml_directory;
423                         dumplist = dumplist->ml_next;
424                 }
425         }
426
427         qsort(dumpv, n, sizeof (char *), dump_cmp);
428         
429         for (i = 0; i < n; i++) {
430                 if (i == 0 || strcmp(dumpv[i], dumpv[i - 1]) != 0)
431                         printf("%s\n", dumpv[i]);
432         }
433         exit(0);
434 }
435