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