2 * showmount.c -- show mount information for an NFS server
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
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)
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.
22 #include <rpc/pmap_prot.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
27 #include <sys/types.h>
34 #include <arpa/inet.h>
41 #define TIMEOUT_TCP 10
42 #define TOTAL_TIMEOUT 20
44 static char * version = "showmount for " VERSION;
45 static char * program_name;
46 static int headers = 1;
52 static struct option longopts[] =
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' },
63 #define MAXHOSTLEN 256
65 static int dump_cmp(const void *pv, const void *qv)
67 const char **p = (const char **)pv;
68 const char **q = (const char **)qv;
69 return strcmp(*p, *q);
72 static void usage(FILE *fp, int n)
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");
81 * Perform a non-blocking connect on the socket fd.
83 * tout contains the timeout. It will be modified to contain the time
84 * remaining (i.e. time provided - time elasped).
86 * Returns zero on success; otherwise, -1 is returned and errno is set
87 * to reflect the nature of the error.
89 static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout)
95 flags = fcntl(fd, F_GETFL, 0);
99 ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
104 * From here on subsequent sys calls could change errno so
105 * we set ret = -errno to capture it in case we decide to
108 len = sizeof(struct sockaddr);
109 ret = connect(fd, (struct sockaddr *)addr, len);
110 if (ret < 0 && errno != EINPROGRESS) {
122 ret = select(fd + 1, NULL, &rset, NULL, tout);
130 if (FD_ISSET(fd, &rset)) {
134 status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
140 /* Oops - something wrong with connect */
148 fcntl(fd, F_SETFL, flags);
152 static unsigned short getport(struct sockaddr_in *addr,
153 unsigned long prog, unsigned long vers, int prot)
156 enum clnt_stat status;
159 struct sockaddr_in laddr, saddr;
160 struct timeval tout = {0, 0};
162 unsigned int send_sz = 0;
163 unsigned int recv_sz = 0;
166 memset(&laddr, 0, sizeof(laddr));
167 memset(&saddr, 0, sizeof(saddr));
168 memset(&parms, 0, sizeof(parms));
170 memcpy(&saddr, addr, sizeof(saddr));
171 saddr.sin_port = htons(PMAPPORT);
173 if (prot == IPPROTO_TCP) {
174 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
176 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
177 rpc_createerr.cf_error.re_errno = errno;
181 tout.tv_sec = TIMEOUT_TCP;
183 ret = connect_nb(sock, &saddr, &tout);
185 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
186 rpc_createerr.cf_error.re_errno = errno;
190 client = clnttcp_create(&saddr,
191 PMAPPROG, PMAPVERS, &sock,
195 * bind to any unused port. If we left this up to the rpc
196 * layer, it would bind to a reserved port, which has been shown
197 * to exhaust the reserved port range in some situations.
199 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
201 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
202 rpc_createerr.cf_error.re_errno = errno;
206 laddr.sin_family = AF_INET;
208 laddr.sin_addr.s_addr = htonl(INADDR_ANY);
210 tout.tv_sec = TIMEOUT_UDP;
212 send_sz = RPCSMALLMSGSIZE;
213 recv_sz = RPCSMALLMSGSIZE;
215 len = sizeof(struct sockaddr_in);
216 if (bind(sock, (struct sockaddr *)&laddr, len) < 0) {
221 client = clntudp_bufcreate(&saddr, PMAPPROG, PMAPVERS,
222 tout, &sock, send_sz, recv_sz);
227 rpc_createerr.cf_stat = RPC_RPCBFAILURE;
231 clnt_control(client, CLSET_FD_CLOSE, NULL);
233 parms.pm_prog = prog;
234 parms.pm_vers = vers;
235 parms.pm_prot = prot;
237 status = clnt_call(client, PMAPPROC_GETPORT,
238 (xdrproc_t) xdr_pmap, (caddr_t) &parms,
239 (xdrproc_t) xdr_u_short, (caddr_t) &port,
242 if (status != RPC_SUCCESS) {
243 clnt_geterr(client, &rpc_createerr.cf_error);
244 rpc_createerr.cf_stat = status;
245 clnt_destroy(client);
247 } else if (port == 0) {
248 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
251 clnt_destroy(client);
256 int main(int argc, char **argv)
258 char hostname_buf[MAXHOSTLEN];
260 enum clnt_stat clnt_stat;
262 struct sockaddr_in server_addr;
264 struct timeval total_timeout;
265 struct timeval pertry_timeout;
269 exports exportlist, exl;
277 program_name = argv[0];
278 while ((c = getopt_long(argc, argv, "adehv", longopts, NULL)) != EOF) {
293 printf("%s\n", version);
306 switch (aflag + dflag + eflag) {
313 fprintf(stderr, "%s: only one of -a, -d or -e is allowed\n",
321 if (gethostname(hostname_buf, MAXHOSTLEN) < 0) {
322 perror("getting hostname");
325 hostname = hostname_buf;
331 fprintf(stderr, "%s: only one hostname is allowed\n",
337 if (inet_aton(hostname, &server_addr.sin_addr)) {
338 server_addr.sin_family = AF_INET;
341 if ((hp = gethostbyname(hostname)) == NULL) {
342 fprintf(stderr, "%s: can't get address for %s\n",
343 program_name, hostname);
346 server_addr.sin_family = AF_INET;
347 memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
350 /* create mount deamon client */
353 msock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
355 server_addr.sin_port = getport(&server_addr,
356 MOUNTPROG, MOUNTVERS, IPPROTO_TCP);
357 if (server_addr.sin_port) {
358 ret = connect_nb(msock, &server_addr, 0);
359 if (ret == 0) /* success */
360 mclient = clnttcp_create(&server_addr,
361 MOUNTPROG, MOUNTVERS, &msock,
370 server_addr.sin_port = getport(&server_addr,
371 MOUNTPROG, MOUNTVERS, IPPROTO_UDP);
372 if (!server_addr.sin_port) {
373 clnt_pcreateerror("showmount");
377 pertry_timeout.tv_sec = TIMEOUT_UDP;
378 pertry_timeout.tv_usec = 0;
379 if ((mclient = clntudp_create(&server_addr,
380 MOUNTPROG, MOUNTVERS, pertry_timeout, &msock)) == NULL) {
381 clnt_pcreateerror("mount clntudp_create");
385 mclient->cl_auth = authunix_create_default();
386 total_timeout.tv_sec = TOTAL_TIMEOUT;
387 total_timeout.tv_usec = 0;
390 memset(&exportlist, '\0', sizeof(exportlist));
392 clnt_stat = clnt_call(mclient, MOUNTPROC_EXPORT,
393 (xdrproc_t) xdr_void, NULL,
394 (xdrproc_t) xdr_exports, (caddr_t) &exportlist,
396 if (clnt_stat != RPC_SUCCESS) {
397 clnt_perror(mclient, "rpc mount export");
398 clnt_destroy(mclient);
402 printf("Export list for %s:\n", hostname);
404 for (exl = exportlist; exl; exl = exl->ex_next) {
405 if ((n = strlen(exl->ex_dir)) > maxlen)
409 printf("%-*s ", maxlen, exportlist->ex_dir);
410 grouplist = exportlist->ex_groups;
413 printf("%s%s", grouplist->gr_name,
414 grouplist->gr_next ? "," : "");
415 grouplist = grouplist->gr_next;
418 printf("(everyone)");
420 exportlist = exportlist->ex_next;
422 clnt_destroy(mclient);
426 memset(&dumplist, '\0', sizeof(dumplist));
427 clnt_stat = clnt_call(mclient, MOUNTPROC_DUMP,
428 (xdrproc_t) xdr_void, NULL,
429 (xdrproc_t) xdr_mountlist, (caddr_t) &dumplist,
431 if (clnt_stat != RPC_SUCCESS) {
432 clnt_perror(mclient, "rpc mount dump");
433 clnt_destroy(mclient);
436 clnt_destroy(mclient);
439 for (list = dumplist; list; list = list->ml_next)
441 dumpv = (char **) calloc(n, sizeof (char *));
443 fprintf(stderr, "%s: out of memory\n", program_name);
450 printf("Hosts on %s:\n", hostname);
452 dumpv[i++] = dumplist->ml_hostname;
453 dumplist = dumplist->ml_next;
458 printf("All mount points on %s:\n", hostname);
462 t=malloc(strlen(dumplist->ml_hostname)+strlen(dumplist->ml_directory)+2);
465 fprintf(stderr, "%s: out of memory\n", program_name);
468 sprintf(t, "%s:%s", dumplist->ml_hostname, dumplist->ml_directory);
470 dumplist = dumplist->ml_next;
475 printf("Directories on %s:\n", hostname);
477 dumpv[i++] = dumplist->ml_directory;
478 dumplist = dumplist->ml_next;
482 qsort(dumpv, n, sizeof (char *), dump_cmp);
484 for (i = 0; i < n; i++) {
485 if (i == 0 || strcmp(dumpv[i], dumpv[i - 1]) != 0)
486 printf("%s\n", dumpv[i]);