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