1/*
2 * Copyright (c) 1999-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright (c) 1997 Apple Computer, Inc. All Rights Reserved
25 *
26 * Copyright (c) 1983, 1989, 1993
27 *	The Regents of the University of California.  All rights reserved.
28 *
29 * This code is derived from software contributed to Berkeley by
30 * Rick Macklem at The University of Guelph.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 *    notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 *    must display the following acknowledgement:
42 *	This product includes software developed by the University of
43 *	California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 *	@(#)nfsstat.c	8.2 (Berkeley) 3/31/95
60 */
61
62
63#include <sys/param.h>
64#include <sys/mount.h>
65#include <sys/sysctl.h>
66#include <sys/time.h>
67#include <sys/socket.h>
68#include <nfs/rpcv2.h>
69#include <nfs/nfsproto.h>
70#include <nfs/nfs.h>
71
72#include <sys/types.h>
73#include <sys/socket.h>
74#include <netinet/in.h>
75#include <arpa/inet.h>
76#include <netdb.h>
77
78#include <signal.h>
79#include <fcntl.h>
80#include <ctype.h>
81#include <errno.h>
82#include <unistd.h>
83#include <stdio.h>
84#include <stdlib.h>
85#include <string.h>
86#include <paths.h>
87#include <pwd.h>
88#include <sys/queue.h>
89#include <err.h>
90
91#define _NFS_XDR_SUBS_FUNCS_ /* define this to get xdrbuf function definitions */
92#include <nfs/xdr_subs.h>
93
94int verbose = 0;
95
96#define AOK	(void *)	// assert alignment is OK
97
98#define SHOW_SERVER 0x01
99#define SHOW_CLIENT 0x02
100#define SHOW_ALL (SHOW_SERVER | SHOW_CLIENT)
101
102#define CLIENT_SERVER_MODE 0
103#define EXPORTS_MODE 1
104#define ACTIVE_USER_MODE 2
105#define MOUNT_MODE 3
106
107#define NUMERIC_NET 1
108#define NUMERIC_USER 2
109
110#define DEFAULT_EXPORT_STATS_BUFLEN 32768
111#define DEFAULT_ACTIVE_USER_STATS_BUFLEN 131072
112#define DEFAULT_MOUNT_INFO_BUFLEN 4096
113
114LIST_HEAD(nfs_active_user_node_head, nfs_active_user_node);
115LIST_HEAD(nfs_export_node_head, nfs_export_node);
116
117struct nfs_active_user_node {
118	LIST_ENTRY(nfs_active_user_node)	user_next;
119        struct nfs_user_stat_user_rec		*rec;
120};
121
122struct nfs_export_node {
123	LIST_ENTRY(nfs_export_node)		export_next;
124	struct nfs_active_user_node_head	nodes;
125        struct nfs_user_stat_path_rec		*rec;
126};
127
128struct nfs_active_user_list {
129	char				*buf;
130	struct nfs_export_node_head	export_list;
131};
132
133void intpr(u_int);
134void printhdr(void);
135void sidewaysintpr(u_int, u_int);
136void usage(void);
137void do_mountinfo(char *);
138void do_exports_interval(u_int interval);
139void do_exports_normal(void);
140void do_active_users_interval(u_int interval, u_int flags);
141void do_active_users_normal(u_int flags);
142int  read_export_stats(char **buf, uint *buflen);
143int  read_active_user_stats(char **buf, uint *buflen);
144void display_export_diffs(char *newb, char *oldb);
145void display_active_user_diffs(char *newb, char *oldb, u_int flags);
146void displayActiveUserRec(struct nfs_user_stat_user_rec *rec, u_int flags);
147struct nfs_export_stat_rec *findExport(char *path, char *buf);
148struct nfs_user_stat_user_rec *findActiveUser(char *path, struct nfs_user_stat_user_rec *rec, char *buf);
149int cmp_active_user(struct nfs_user_stat_user_rec *rec1, struct nfs_user_stat_user_rec *rec2);
150uint64_t serialdiff_64(uint64_t new, uint64_t old);
151struct nfs_export_node_head *get_sorted_active_user_list(char *buf);
152void free_nfs_export_list(struct nfs_export_node_head *export_list);
153void catchalarm(int);
154
155int
156main(int argc, char *argv[])
157{
158	extern int optind;
159	extern char *optarg;
160	u_int interval;
161	u_int display = SHOW_ALL;
162	u_int usermode_flags = 0;
163	u_int mode = CLIENT_SERVER_MODE;
164	int ch;
165
166	interval = 0;
167	while ((ch = getopt(argc, argv, "w:sceun:mv")) != EOF)
168		switch(ch) {
169		case 'v':
170			verbose++;
171			break;
172		case 'w':
173			interval = atoi(optarg);
174			break;
175		case 's':
176			mode = CLIENT_SERVER_MODE;
177			display = SHOW_SERVER;
178			break;
179		case 'c':
180			mode = CLIENT_SERVER_MODE;
181			display = SHOW_CLIENT;
182			break;
183		case 'e':
184			mode = EXPORTS_MODE;
185			break;
186		case 'u':
187			mode = ACTIVE_USER_MODE;
188			break;
189		case 'm':
190			mode = MOUNT_MODE;
191			break;
192		case 'n':
193			if (!strcmp(optarg, "net"))
194				usermode_flags |= NUMERIC_NET;
195			else if (!strcmp(optarg, "user"))
196				usermode_flags |= NUMERIC_USER;
197			else
198				printf("unsupported 'n' argument\n");
199			break;
200		case '?':
201		default:
202			usage();
203		}
204	argc -= optind;
205	argv += optind;
206
207	/* Determine the mode and display the stats */
208	if (mode == MOUNT_MODE) {
209		do_mountinfo(argc ? argv[0] : NULL);
210	} else if (mode == EXPORTS_MODE) {
211		if (interval)
212			do_exports_interval(interval);
213		else
214			do_exports_normal();
215	} else if (mode == ACTIVE_USER_MODE) {
216		if (interval)
217			do_active_users_interval(interval, usermode_flags);
218		else
219			do_active_users_normal(usermode_flags);
220	} else {
221		if (interval)
222			sidewaysintpr(interval, display);
223		else
224			intpr(display);
225	}
226	exit(0);
227}
228
229/*
230 * Read the nfs stats using sysctl(3)
231 */
232void
233readstats(struct nfsstats *stp)
234{
235	int name[3];
236	size_t buflen = sizeof(*stp);
237	struct vfsconf vfc;
238
239	if (getvfsbyname("nfs", &vfc) < 0)
240		err(1, "getvfsbyname: NFS not compiled into kernel");
241	name[0] = CTL_VFS;
242	name[1] = vfc.vfc_typenum;
243	name[2] = NFS_NFSSTATS;
244	if (sysctl(name, 3, stp, &buflen, NULL, 0) < 0)
245		err(1, "sysctl");
246}
247
248/*
249 * Read the nfs export stats from the kernel.
250 *
251 * If a valid buffer (non-NULL) is passed in parameter buf, then
252 * the parameter buflen must also be valid. In this case an attempt will
253 * be made to read the export stat records from the kernel using the passed buffer.
254 * However if the passed buffer is of insufficient size to hold all available
255 * export stat records, then the passed buffer will be freed and a new one
256 * will be allocated.  Thus the returned buffer may not be the same as
257 * the buffer originally passed in the function call.
258 *
259 * If a NULL buffer is passed in parameter buf, a new buffer will
260 * be allocated.
261 *
262 * Returns:
263 *  0	if successful,  valid buffer containing export stat records returned in buf, and buflen is valid.
264 *  1	if not enough memory was available.  NULL buffer returned in buf,  buflen is undefined.
265 */
266int
267read_export_stats(char **buf, uint *buflen)
268{
269	struct nfs_export_stat_desc *hdr;
270	int	name[3];
271	size_t	statlen;
272	struct	vfsconf vfc;
273
274	/* check if we need to allocate a buffer */
275	if (*buf == NULL) {
276		*buflen = DEFAULT_EXPORT_STATS_BUFLEN;
277		*buf = malloc(*buflen);
278		if (*buf == NULL) {
279			warnx("No memory for reading export stat data");
280			return 1;
281		}
282		/* make sure to touch/init the entire buffer */
283		bzero(*buf, *buflen);
284	}
285
286	if (getvfsbyname("nfs", &vfc) < 0)
287	{
288		free(*buf);
289		err(1, "getvfsbyname: NFS not compiled into kernel");
290	}
291
292	name[0] = CTL_VFS;
293	name[1] = vfc.vfc_typenum;
294	name[2] = NFS_EXPORTSTATS;
295
296	/* fetch the export stats */
297	statlen = *buflen;
298	if (sysctl(name, 3, *buf, (size_t *)&statlen, NULL, 0) < 0) {
299		/* sysctl failed */
300		free(*buf);
301		err(1, "sysctl failed");
302	}
303
304	/* check if buffer was large enough */
305	if (statlen > *buflen) {
306		/* Didn't get all the stat records, try again with a larger buffer */
307		/* Alloc a larger buffer than the sysctl indicated, in case more exports */
308		/* get added before we make the next sysctl */
309		free(*buf);
310		*buflen = statlen + (4 * sizeof(struct nfs_export_stat_rec));
311		*buf = malloc(*buflen);
312		if (*buf == NULL) {
313			warnx("No memory for reading export statistics");
314			return 1;
315		}
316		/* make sure to touch/init the entire buffer */
317		bzero(*buf, *buflen);
318
319		/* fetch export stats one more time */
320		statlen = *buflen;
321		if (sysctl(name, 3, *buf, (size_t *)&statlen, NULL, 0) < 0) {
322			free(*buf);
323			err(1, "sysctl failed");
324		}
325	}
326
327	/* Check export stat record version */
328	hdr = (struct nfs_export_stat_desc *)*buf;
329	if (hdr->rec_vers != NFS_EXPORT_STAT_REC_VERSION) {
330		free(*buf);
331		errx(1, "NFS export stat version mismatch");
332	}
333
334	return 0;
335}
336
337/*
338 * Read nfs active user stats from the kernel.
339 *
340 * If a valid buffer (non-NULL) is passed in parameter buf, then
341 * the parameter buflen must also be valid. In this case an attempt will
342 * be made to read the user stat records from the kernel using the passed buffer.
343 * However if the passed buffer is of insufficient size to hold all available
344 * user stat records, then the passed buffer will be freed and a new one
345 * will be allocated.  Thus the returned buffer may not be the same as
346 * the buffer originally passed in the function call.
347 *
348 * If a NULL buffer is passed in parameter buf, a new buffer will
349 * be allocated.
350 *
351 * Returns:
352 *  0   if successful,  valid buffer containing export active user stat records returned in buf, and buflen is valid.
353 *  1   if not enough memory was available.  NULL buffer returned in buf,  buflen is undefined.
354 */
355int
356read_active_user_stats(char **buf, uint *buflen)
357{
358	struct nfs_user_stat_desc	*hdr;
359	int				name[3];
360	size_t				statlen;
361	struct				vfsconf vfc;
362
363	/* check if we need to allocate a buffer */
364	if (*buf == NULL) {
365		*buflen = DEFAULT_ACTIVE_USER_STATS_BUFLEN;
366		*buf = malloc(*buflen);
367		if (*buf == NULL) {
368			warnx("No memory for reading active user statistics");
369			return 1;
370		}
371		/* make sure to touch/init the entire buffer */
372		bzero(*buf, *buflen);
373	}
374
375	if (getvfsbyname("nfs", &vfc) < 0)
376	{
377		free(*buf);
378		err(1, "getvfsbyname: NFS not compiled into kernel");
379	}
380
381	name[0] = CTL_VFS;
382	name[1] = vfc.vfc_typenum;
383	name[2] = NFS_USERSTATS;
384
385	/* fetch the user stats */
386	statlen = *buflen;
387	if (sysctl(name, 3, *buf, (size_t *)&statlen, NULL, 0) < 0) {
388		/* sysctl failed */
389		free(*buf);
390		err(1, "sysctl failed");
391	}
392
393	/* check if buffer was large enough */
394	if (statlen > *buflen) {
395		/* Didn't get all the stat records, try again with a larger buffer. */
396		free(*buf);
397
398		/* Allocate a little extra than indicated in case more exports and/or users */
399		/* show up before the next sysctl */
400		*buflen = statlen + (5 * sizeof(struct nfs_user_stat_path_rec));
401		*buf = malloc(*buflen);
402		if (*buf == NULL) {
403			warnx("No memory for reading active user statistics\n");
404			return 1;
405		}
406		/* make sure to touch/init the entire buffer */
407		bzero(*buf, *buflen);
408
409		/* fetch user stats one more time */
410		statlen = *buflen;
411		if (sysctl(name, 3, *buf, (size_t *)&statlen, NULL, 0) < 0) {
412			free(*buf);
413			err(1, "sysctl failed");
414		}
415	}
416
417	/* Check record version */
418	hdr = (struct nfs_user_stat_desc *)*buf;
419	if (hdr->rec_vers != NFS_USER_STAT_REC_VERSION) {
420		free(*buf);
421		errx(1, "NFS user stat version mismatch");
422	}
423
424	return 0;
425}
426
427/*
428 * Read nfs mount info from the kernel.
429 *
430 * If a valid buffer (non-NULL) is passed in parameter buf, then
431 * the parameter buflen must also be valid. In this case an attempt will
432 * be made to read the mount info from the kernel using the passed buffer.
433 * However if the passed buffer is of insufficient size to hold the entire
434 * mount info record, then the passed buffer will be freed and a new one
435 * will be allocated.  Thus the returned buffer may not be the same as
436 * the buffer originally passed in the function call.
437 *
438 * If a NULL buffer is passed in parameter buf, a new buffer will
439 * be allocated.
440 *
441 * Returns:
442 *  0   if successful, valid buffer containing mount info returned in buf, and buflen is valid.
443 *  1   if not enough memory was available.  NULL buffer returned in buf,  buflen is undefined.
444 */
445int
446read_mountinfo(fsid_t *fsid, char **buf, uint *buflen)
447{
448	uint32_t vers, *bufxdr;
449	int name[3];
450	size_t infolen;
451	struct vfsconf vfc;
452
453	/* check if we need to allocate a buffer */
454	if (*buf == NULL) {
455		*buflen = DEFAULT_MOUNT_INFO_BUFLEN;
456		*buf = malloc(*buflen);
457		if (*buf == NULL) {
458			warnx("No memory for reading mount information");
459			return (ENOMEM);
460		}
461		/* make sure to touch/init the entire buffer */
462		bzero(*buf, *buflen);
463	}
464
465	if (getvfsbyname("nfs", &vfc) < 0)
466		err(1, "getvfsbyname: NFS not compiled into kernel");
467
468	name[0] = CTL_VFS;
469	name[1] = vfc.vfc_typenum;
470	name[2] = NFS_MOUNTINFO;
471
472	/* copy fsid to buffer to tell kernel which fs to read info for */
473	bufxdr = (uint32_t*) AOK *buf;
474	bufxdr[0] = htonl(fsid->val[0]);
475	bufxdr[1] = htonl(fsid->val[1]);
476
477	/* fetch the mount info */
478	infolen = *buflen;
479	if (sysctl(name, 3, *buf, &infolen, NULL, 0) < 0) {
480		/* sysctl failed */
481		warn("sysctl failed");
482		return (errno);
483	}
484
485	/* check if buffer was large enough */
486	if (infolen > *buflen) {
487		/* Didn't have large enough buffer, try again with a larger buffer. */
488		free(*buf);
489
490		*buflen = infolen;
491		*buf = malloc(*buflen);
492		if (*buf == NULL) {
493			warnx("No memory for reading mount information\n");
494			return (ENOMEM);
495		}
496		/* make sure to touch/init the entire buffer */
497		bzero(*buf, *buflen);
498
499		/* copy fsid to buffer to tell kernel which fs to read info for */
500		bufxdr = (uint32_t*) AOK *buf;
501		bufxdr[0] = htonl(fsid->val[0]);
502		bufxdr[1] = htonl(fsid->val[1]);
503
504		/* fetch mount information one more time */
505		infolen = *buflen;
506		if (sysctl(name, 3, *buf, (size_t *)&infolen, NULL, 0) < 0) {
507			warn("sysctl failed");
508			return (errno);
509		}
510	}
511
512	/* Check mount information version */
513	vers = ntohl(*(uint32_t*) AOK *buf);
514	if (vers != NFS_MOUNT_INFO_VERSION) {
515		warnx("NFS mount information version mismatch");
516		return (EBADRPC);
517	}
518
519	return (0);
520}
521
522/*
523 * Print a description of the nfs stats.
524 */
525void
526intpr(u_int display)
527{
528	struct nfsstats nfsstats;
529
530	readstats(&nfsstats);
531
532	if (display & SHOW_CLIENT) {
533		printf("Client Info:\n");
534		printf("RPC Counts:\n");
535		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
536		       "Getattr", "Setattr", "Lookup", "Readlink", "Read",
537		       "Write");
538		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
539		       nfsstats.rpccnt[NFSPROC_GETATTR],
540		       nfsstats.rpccnt[NFSPROC_SETATTR],
541		       nfsstats.rpccnt[NFSPROC_LOOKUP],
542		       nfsstats.rpccnt[NFSPROC_READLINK],
543		       nfsstats.rpccnt[NFSPROC_READ],
544		       nfsstats.rpccnt[NFSPROC_WRITE]);
545		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
546		       "Create", "Remove", "Rename", "Link", "Symlink", "Mkdir");
547		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
548		       nfsstats.rpccnt[NFSPROC_CREATE],
549		       nfsstats.rpccnt[NFSPROC_REMOVE],
550		       nfsstats.rpccnt[NFSPROC_RENAME],
551		       nfsstats.rpccnt[NFSPROC_LINK],
552		       nfsstats.rpccnt[NFSPROC_SYMLINK],
553		       nfsstats.rpccnt[NFSPROC_MKDIR]);
554		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
555		       "Rmdir", "Readdir", "RdirPlus", "Access",
556		       "Mknod", "Fsstat");
557		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
558		       nfsstats.rpccnt[NFSPROC_RMDIR],
559		       nfsstats.rpccnt[NFSPROC_READDIR],
560		       nfsstats.rpccnt[NFSPROC_READDIRPLUS],
561		       nfsstats.rpccnt[NFSPROC_ACCESS],
562		       nfsstats.rpccnt[NFSPROC_MKNOD],
563		       nfsstats.rpccnt[NFSPROC_FSSTAT]);
564
565		printf("%12.12s %12.12s %12.12s\n",
566		       "Fsinfo", "PathConf", "Commit");
567		printf("%12llu %12llu %12llu\n",
568		       nfsstats.rpccnt[NFSPROC_FSINFO],
569		       nfsstats.rpccnt[NFSPROC_PATHCONF],
570		       nfsstats.rpccnt[NFSPROC_COMMIT]);
571		printf("RPC Info:\n");
572		printf("%12.12s %12.12s %12.12s %12.12s %12.12s\n",
573		       "TimedOut", "Invalid", "X Replies", "Retries", "Requests");
574		printf("%12llu %12llu %12llu %12llu %12llu\n",
575		       nfsstats.rpctimeouts,
576		       nfsstats.rpcinvalid,
577		       nfsstats.rpcunexpected,
578		       nfsstats.rpcretries,
579		       nfsstats.rpcrequests);
580		printf("Cache Info:\n");
581		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
582		       "Attr Hits", "Misses", "Lkup Hits", "Misses", "BioR Hits", "Misses");
583		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
584		       nfsstats.attrcache_hits, nfsstats.attrcache_misses,
585		       nfsstats.lookupcache_hits, nfsstats.lookupcache_misses,
586		       nfsstats.biocache_reads-nfsstats.read_bios,
587		       nfsstats.read_bios);
588		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
589		       "BioW Hits", "Misses", "BioRLHits", "Misses", "BioD Hits", "Misses");
590		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
591		       nfsstats.biocache_writes-nfsstats.write_bios,
592		       nfsstats.write_bios,
593		       nfsstats.biocache_readlinks-nfsstats.readlink_bios,
594		       nfsstats.readlink_bios,
595		       nfsstats.biocache_readdirs-nfsstats.readdir_bios,
596		       nfsstats.readdir_bios);
597		printf("%12.12s %12.12s\n", "DirE Hits", "Misses");
598		printf("%12llu %12llu\n",
599		       nfsstats.direofcache_hits, nfsstats.direofcache_misses);
600	}
601	if (display & SHOW_SERVER) {
602		printf("\nServer Info:\n");
603		printf("RPC Counts:\n");
604		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
605		       "Getattr", "Setattr", "Lookup", "Readlink", "Read",
606		       "Write");
607		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
608		       nfsstats.srvrpccnt[NFSPROC_GETATTR],
609		       nfsstats.srvrpccnt[NFSPROC_SETATTR],
610		       nfsstats.srvrpccnt[NFSPROC_LOOKUP],
611		       nfsstats.srvrpccnt[NFSPROC_READLINK],
612		       nfsstats.srvrpccnt[NFSPROC_READ],
613		       nfsstats.srvrpccnt[NFSPROC_WRITE]);
614		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
615		       "Create", "Remove", "Rename", "Link", "Symlink", "Mkdir");
616		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
617		       nfsstats.srvrpccnt[NFSPROC_CREATE],
618		       nfsstats.srvrpccnt[NFSPROC_REMOVE],
619		       nfsstats.srvrpccnt[NFSPROC_RENAME],
620		       nfsstats.srvrpccnt[NFSPROC_LINK],
621		       nfsstats.srvrpccnt[NFSPROC_SYMLINK],
622		       nfsstats.srvrpccnt[NFSPROC_MKDIR]);
623		printf("%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
624		       "Rmdir", "Readdir", "RdirPlus", "Access", "Mknod", "Fsstat");
625		printf("%12llu %12llu %12llu %12llu %12llu %12llu\n",
626		       nfsstats.srvrpccnt[NFSPROC_RMDIR],
627		       nfsstats.srvrpccnt[NFSPROC_READDIR],
628		       nfsstats.srvrpccnt[NFSPROC_READDIRPLUS],
629		       nfsstats.srvrpccnt[NFSPROC_ACCESS],
630		       nfsstats.srvrpccnt[NFSPROC_MKNOD],
631		       nfsstats.srvrpccnt[NFSPROC_FSSTAT]);
632		printf("%12.12s %12.12s %12.12s\n",
633		       "Fsinfo", "PathConf", "Commit");
634		printf("%12llu %12llu %12llu\n",
635		       nfsstats.srvrpccnt[NFSPROC_FSINFO],
636		       nfsstats.srvrpccnt[NFSPROC_PATHCONF],
637		       nfsstats.srvrpccnt[NFSPROC_COMMIT]);
638		printf("Server Ret-Failed\n");
639		printf("%17llu\n", nfsstats.srvrpc_errs);
640		printf("Server Faults\n");
641		printf("%13llu\n", nfsstats.srv_errs);
642		printf("Server Cache Stats:\n");
643		printf("%12.12s %12.12s %12.12s %12.12s\n",
644		       "Inprog", "Idem", "Non-idem", "Misses");
645		printf("%12llu %12llu %12llu %12llu\n",
646		       nfsstats.srvcache_inproghits,
647		       nfsstats.srvcache_idemdonehits,
648		       nfsstats.srvcache_nonidemdonehits,
649		       nfsstats.srvcache_misses);
650		printf("Server Write Gathering:\n");
651		printf("%12.12s %12.12s %12.12s\n",
652		       "WriteOps", "WriteRPC", "Opsaved");
653		printf("%12llu %12llu %12llu\n",
654		       nfsstats.srvvop_writes,
655		       nfsstats.srvrpccnt[NFSPROC_WRITE],
656		       nfsstats.srvrpccnt[NFSPROC_WRITE] - nfsstats.srvvop_writes);
657	}
658}
659
660u_char	signalled;			/* set if alarm goes off "early" */
661
662/*
663 * Print a running summary of nfs statistics.
664 * Repeat display every interval seconds, showing statistics
665 * collected over that interval.  Assumes that interval is non-zero.
666 * First line printed at top of screen is always cumulative.
667 */
668void
669sidewaysintpr(u_int interval, u_int display)
670{
671	struct nfsstats nfsstats, lastst;
672	int hdrcnt;
673	sigset_t sigset, oldsigset;
674
675	signal(SIGALRM, catchalarm);
676	signalled = 0;
677	alarm(interval);
678	bzero((caddr_t)&lastst, sizeof(lastst));
679
680	for (hdrcnt = 1;;) {
681		if (!--hdrcnt) {
682			printhdr();
683			hdrcnt = 20;
684		}
685		readstats(&nfsstats);
686		if (display & SHOW_CLIENT)
687		  printf("Client: %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu\n",
688		    nfsstats.rpccnt[NFSPROC_GETATTR]-lastst.rpccnt[NFSPROC_GETATTR],
689		    nfsstats.rpccnt[NFSPROC_LOOKUP]-lastst.rpccnt[NFSPROC_LOOKUP],
690		    nfsstats.rpccnt[NFSPROC_READLINK]-lastst.rpccnt[NFSPROC_READLINK],
691		    nfsstats.rpccnt[NFSPROC_READ]-lastst.rpccnt[NFSPROC_READ],
692		    nfsstats.rpccnt[NFSPROC_WRITE]-lastst.rpccnt[NFSPROC_WRITE],
693		    nfsstats.rpccnt[NFSPROC_RENAME]-lastst.rpccnt[NFSPROC_RENAME],
694		    nfsstats.rpccnt[NFSPROC_ACCESS]-lastst.rpccnt[NFSPROC_ACCESS],
695		    (nfsstats.rpccnt[NFSPROC_READDIR]-lastst.rpccnt[NFSPROC_READDIR])
696		    +(nfsstats.rpccnt[NFSPROC_READDIRPLUS]-lastst.rpccnt[NFSPROC_READDIRPLUS]));
697		if (display & SHOW_SERVER)
698		  printf("Server: %8llu %8llu %8llu %8llu %8llu %8llu %8llu %8llu\n",
699		    nfsstats.srvrpccnt[NFSPROC_GETATTR]-lastst.srvrpccnt[NFSPROC_GETATTR],
700		    nfsstats.srvrpccnt[NFSPROC_LOOKUP]-lastst.srvrpccnt[NFSPROC_LOOKUP],
701		    nfsstats.srvrpccnt[NFSPROC_READLINK]-lastst.srvrpccnt[NFSPROC_READLINK],
702		    nfsstats.srvrpccnt[NFSPROC_READ]-lastst.srvrpccnt[NFSPROC_READ],
703		    nfsstats.srvrpccnt[NFSPROC_WRITE]-lastst.srvrpccnt[NFSPROC_WRITE],
704		    nfsstats.srvrpccnt[NFSPROC_RENAME]-lastst.srvrpccnt[NFSPROC_RENAME],
705		    nfsstats.srvrpccnt[NFSPROC_ACCESS]-lastst.srvrpccnt[NFSPROC_ACCESS],
706		    (nfsstats.srvrpccnt[NFSPROC_READDIR]-lastst.srvrpccnt[NFSPROC_READDIR])
707		    +(nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]-lastst.srvrpccnt[NFSPROC_READDIRPLUS]));
708		lastst = nfsstats;
709		fflush(stdout);
710		sigemptyset(&sigset);
711		sigaddset(&sigset, SIGALRM);
712		if (sigprocmask(SIG_BLOCK, &sigset, &oldsigset) == -1)
713			err(1, "sigprocmask failed");
714		if (!signalled) {
715			sigemptyset(&sigset);
716			sigsuspend(&sigset);
717		}
718		if (sigprocmask(SIG_SETMASK, &oldsigset, NULL) == -1)
719			err(1, "sigprocmask failed");
720		signalled = 0;
721		alarm(interval);
722	}
723	/*NOTREACHED*/
724}
725
726/* ****************** */
727/* *** Mount Info *** */
728/* ****************** */
729
730struct nfs_fs_server {
731	char *		name;					/* server name */
732	char **		addrs;					/* array of addresses */
733	uint32_t	addrcount;				/* # of addresses */
734};
735
736struct nfs_fs_location {
737	struct nfs_fs_server *servers;				/* array of servers */
738	char **		components;				/* array of path components */
739	uint32_t	servcount;				/* # of servers */
740	uint32_t	compcount;				/* # of path components */
741};
742
743struct mountargs {
744	uint32_t	margsvers;				/* what mount args version was used */
745	int		mntflags;				/* MNT_* flags */
746	uint32_t	mattrs[NFS_MATTR_BITMAP_LEN];		/* what attrs are set */
747	uint32_t	mflags_mask[NFS_MFLAG_BITMAP_LEN];	/* what flags are set */
748	uint32_t	mflags[NFS_MFLAG_BITMAP_LEN];		/* set flag values */
749	char *		realm;					/* realm to use for acquiring creds */
750	char *		principal;				/* principal to use on initial mount */
751	char *		sprinc;					/* server's kerberos principal */
752	uint32_t	nfs_version, nfs_minor_version;		/* NFS version */
753	uint32_t	rsize, wsize, readdirsize, readahead;	/* I/O values */
754	struct timespec	acregmin, acregmax, acdirmin, acdirmax;	/* attrcache values */
755	uint32_t	lockmode;				/* advisory file locking mode */
756	struct nfs_sec	sec;					/* security flavors */
757	uint32_t	maxgrouplist;				/* max AUTH_SYS groups */
758	char		sotype[6];				/* socket type */
759	uint32_t	nfs_port, mount_port;			/* port info */
760	struct timespec	request_timeout;			/* NFS request timeout */
761	uint32_t	soft_retry_count;			/* soft retrans count */
762	struct timespec	dead_timeout;				/* dead timeout value */
763	fhandle_t	fh;					/* initial file handle */
764	uint32_t	numlocs;				/* # of fs locations */
765	struct nfs_fs_location *locs;				/* array of fs locations */
766	char *		mntfrom;				/* mntfrom mount arg */
767};
768
769void
770mountargs_cleanup(struct mountargs *margs)
771{
772	uint32_t loc, serv, addr, comp;
773
774	for (loc=0; loc < margs->numlocs; loc++) {
775		if (!margs->locs)
776			break;
777		for (serv=0; serv < margs->locs[loc].servcount; serv++) {
778			if (!margs->locs[loc].servers)
779				break;
780			for (addr=0; addr < margs->locs[loc].servers[serv].addrcount; addr++) {
781				if (!margs->locs[loc].servers[serv].addrs || !margs->locs[loc].servers[serv].addrs[addr])
782					continue;
783				free(margs->locs[loc].servers[serv].addrs[addr]);
784				margs->locs[loc].servers[serv].addrs[addr] = NULL;
785			}
786			if (margs->locs[loc].servers[serv].addrs) {
787				free(margs->locs[loc].servers[serv].addrs);
788				margs->locs[loc].servers[serv].addrs = NULL;
789			}
790			margs->locs[loc].servers[serv].addrcount = 0;
791			if (margs->locs[loc].servers[serv].name) {
792				free(margs->locs[loc].servers[serv].name);
793				margs->locs[loc].servers[serv].name = NULL;
794			}
795		}
796		if (margs->locs[loc].servers) {
797			free(margs->locs[loc].servers);
798			margs->locs[loc].servers = NULL;
799		}
800		margs->locs[loc].servcount = 0;
801		for (comp=0; comp < margs->locs[loc].compcount; comp++) {
802			if (!margs->locs[loc].components || !margs->locs[loc].components[comp])
803				continue;
804			if (margs->locs[loc].components[comp]) {
805				free(margs->locs[loc].components[comp]);
806				margs->locs[loc].components[comp] = NULL;
807			}
808		}
809		if (margs->locs[loc].components) {
810			free(margs->locs[loc].components);
811			margs->locs[loc].components = NULL;
812		}
813		margs->locs[loc].compcount = 0;
814	}
815	if (margs->locs) {
816		free(margs->locs);
817		margs->locs = NULL;
818	}
819	margs->numlocs = 0;
820	if (margs->mntfrom) {
821		free(margs->mntfrom);
822		margs->mntfrom = NULL;
823	}
824
825	if (margs->realm) {
826		free(margs->realm);
827		margs->realm = NULL;
828	}
829	if (margs->principal) {
830		free(margs->principal);
831		margs->principal = NULL;
832	}
833	if (margs->sprinc) {
834		free(margs->sprinc);
835		margs->sprinc = NULL;
836	}
837}
838
839int
840parse_mountargs(struct xdrbuf *xb, int margslen, struct mountargs *margs)
841{
842	uint32_t val = 0, attrslength = 0, loc, serv, addr, comp;
843	int error = 0, i;
844
845	if (margslen <= XDRWORD*2)
846		return (EBADRPC);
847	xb_get_32(error, xb, margs->margsvers);			/* mount args version */
848	if (margs->margsvers > NFS_ARGSVERSION_XDR)
849		return (EBADRPC);
850	xb_get_32(error, xb, val);				/* mount args length */
851	if (val != (uint32_t)margslen)
852		return (EBADRPC);
853	xb_get_32(error, xb, val);				/* xdr args version */
854	if (val != NFS_XDRARGS_VERSION_0)
855		return (EINVAL);
856	val = NFS_MATTR_BITMAP_LEN;
857	xb_get_bitmap(error, xb, margs->mattrs, val);
858	xb_get_32(error, xb, attrslength);
859	if (attrslength > ((uint32_t)margslen - ((4+NFS_MATTR_BITMAP_LEN+1)*XDRWORD)))
860		return (EINVAL);
861	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_FLAGS)) {
862		val = NFS_MFLAG_BITMAP_LEN;
863		xb_get_bitmap(error, xb, margs->mflags_mask, val);
864		val = NFS_MFLAG_BITMAP_LEN;
865		xb_get_bitmap(error, xb, margs->mflags, val);
866	}
867	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_NFS_VERSION))
868		xb_get_32(error, xb, margs->nfs_version);
869	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_NFS_MINOR_VERSION))
870		xb_get_32(error, xb, margs->nfs_minor_version);
871	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_READ_SIZE))
872		xb_get_32(error, xb, margs->rsize);
873	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_WRITE_SIZE))
874		xb_get_32(error, xb, margs->wsize);
875	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_READDIR_SIZE))
876		xb_get_32(error, xb, margs->readdirsize);
877	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_READAHEAD))
878		xb_get_32(error, xb, margs->readahead);
879	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_REG_MIN)) {
880		xb_get_32(error, xb, margs->acregmin.tv_sec);
881		xb_get_32(error, xb, margs->acregmin.tv_nsec);
882	}
883	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_REG_MAX)) {
884		xb_get_32(error, xb, margs->acregmax.tv_sec);
885		xb_get_32(error, xb, margs->acregmax.tv_nsec);
886	}
887	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN)) {
888		xb_get_32(error, xb, margs->acdirmin.tv_sec);
889		xb_get_32(error, xb, margs->acdirmin.tv_nsec);
890	}
891	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX)) {
892		xb_get_32(error, xb, margs->acdirmax.tv_sec);
893		xb_get_32(error, xb, margs->acdirmax.tv_nsec);
894	}
895	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_LOCK_MODE))
896		xb_get_32(error, xb, margs->lockmode);
897	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SECURITY)) {
898		xb_get_32(error, xb, margs->sec.count);
899		for (i=0; i < margs->sec.count; i++)
900			xb_get_32(error, xb, margs->sec.flavors[i]);
901	}
902	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_MAX_GROUP_LIST))
903		xb_get_32(error, xb, margs->maxgrouplist);
904	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SOCKET_TYPE)) {
905		xb_get_32(error, xb, val);		/* socket type length */
906		if (val >= sizeof(margs->sotype))
907			error = EBADRPC;
908		if (!error)
909			error = xb_get_bytes(xb, margs->sotype, val, 0);
910		margs->sotype[val] = '\0';
911	}
912	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_NFS_PORT))
913		xb_get_32(error, xb, margs->nfs_port);
914	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_MOUNT_PORT))
915		xb_get_32(error, xb, margs->mount_port);
916	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_REQUEST_TIMEOUT)) {
917		xb_get_32(error, xb, margs->request_timeout.tv_sec);
918		xb_get_32(error, xb, margs->request_timeout.tv_nsec);
919	}
920	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SOFT_RETRY_COUNT))
921		xb_get_32(error, xb, margs->soft_retry_count);
922	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_DEAD_TIMEOUT)) {
923		xb_get_32(error, xb, margs->dead_timeout.tv_sec);
924		xb_get_32(error, xb, margs->dead_timeout.tv_nsec);
925	}
926	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_FH)) {
927		xb_get_32(error, xb, margs->fh.fh_len);
928		if (!error)
929			error = xb_get_bytes(xb, (char*)&margs->fh.fh_data[0], margs->fh.fh_len, 0);
930	}
931	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_FS_LOCATIONS)) {
932		xb_get_32(error, xb, margs->numlocs);                        /* FS_LOCATIONS */
933		if (!error && (margs->numlocs > 255))
934			error = EINVAL;
935		if (!error && margs->numlocs) {
936			margs->locs = calloc(margs->numlocs, sizeof(struct nfs_fs_location));
937			if (!margs->locs)
938				error = ENOMEM;
939		}
940		for (loc = 0; !error && (loc < margs->numlocs); loc++) {
941			xb_get_32(error, xb, margs->locs[loc].servcount);
942			if (!error && (margs->locs[loc].servcount > 255))
943				error = EINVAL;
944			if (!error && margs->locs[loc].servcount) {
945				margs->locs[loc].servers = calloc(margs->locs[loc].servcount, sizeof(struct nfs_fs_server));
946				if (!margs->locs[loc].servers)
947					error = ENOMEM;
948			}
949			for (serv = 0; !error && (serv < margs->locs[loc].servcount); serv++) {
950				xb_get_32(error, xb, val);
951				if (!error && (val > MAXPATHLEN))
952					error = EINVAL;
953				if (!error && val) {
954					margs->locs[loc].servers[serv].name = calloc(1, val+1);
955					if (!margs->locs[loc].servers[serv].name)
956						error = ENOMEM;
957				}
958				if (!error)
959					error = xb_get_bytes(xb, margs->locs[loc].servers[serv].name, val, 0);
960				xb_get_32(error, xb, margs->locs[loc].servers[serv].addrcount);
961				if (!error && (margs->locs[loc].servers[serv].addrcount > 255))
962					error = EINVAL;
963				if (!error && margs->locs[loc].servers[serv].addrcount) {
964					margs->locs[loc].servers[serv].addrs = calloc(margs->locs[loc].servers[serv].addrcount, sizeof(char*));
965					if (!margs->locs[loc].servers[serv].addrs)
966						error = ENOMEM;
967				}
968				for (addr = 0; !error && (addr < margs->locs[loc].servers[serv].addrcount); addr++) {
969					xb_get_32(error, xb, val);
970					if (!error && (val > 255))
971						error = EINVAL;
972					if (!error && val) {
973						margs->locs[loc].servers[serv].addrs[addr] = calloc(1, val+1);
974						if (!margs->locs[loc].servers[serv].addrs[addr])
975							error = ENOMEM;
976					}
977					if (!error)
978						error = xb_get_bytes(xb, margs->locs[loc].servers[serv].addrs[addr], val, 0);
979				}
980				xb_get_32(error, xb, val);
981				xb_skip(error, xb, val);	/* skip server info */
982			}
983			xb_get_32(error, xb, margs->locs[loc].compcount);
984			if (!error && (val > MAXPATHLEN))
985				error = EINVAL;
986			if (!error && margs->locs[loc].compcount) {
987				margs->locs[loc].components = calloc(margs->locs[loc].compcount, sizeof(char*));
988				if (!margs->locs[loc].components)
989					error = ENOMEM;
990			}
991			for (comp = 0; !error && (comp < margs->locs[loc].compcount); comp++) {
992				xb_get_32(error, xb, val);
993				if (!error && (val > MAXPATHLEN))
994					error = EINVAL;
995				if (!error && val) {
996					margs->locs[loc].components[comp] = calloc(1, val+1);
997					if (!margs->locs[loc].components[comp])
998						error = ENOMEM;
999				}
1000				if (!error)
1001					error = xb_get_bytes(xb, margs->locs[loc].components[comp], val, 0);
1002			}
1003			xb_get_32(error, xb, val);
1004			xb_skip(error, xb, val);	/* skip fs loction info */
1005		}
1006	}
1007	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_MNTFLAGS))
1008		xb_get_32(error, xb, margs->mntflags);
1009	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_MNTFROM)) {
1010		xb_get_32(error, xb, val);
1011		if (!error)
1012			margs->mntfrom = calloc(1, val+1);
1013		if (!margs->mntfrom)
1014			error = ENOMEM;
1015		if (!error)
1016			error = xb_get_bytes(xb, margs->mntfrom, val, 0);
1017	}
1018
1019	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_REALM)) {
1020		xb_get_32(error, xb, val);
1021		if (!error && ((val < 1) || (val > MAXPATHLEN)))
1022			error=EINVAL;
1023		margs->realm = calloc(val+1, sizeof(char));
1024		if (!margs->realm)
1025			error = ENOMEM;
1026		if (!error)
1027			error = xb_get_bytes(xb, margs->realm, val, 0);
1028	}
1029
1030	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_PRINCIPAL)) {
1031		xb_get_32(error, xb, val);
1032		if (!error && ((val < 1) || (val > MAXPATHLEN)))
1033			error=EINVAL;
1034		margs->principal = calloc(val+1, sizeof(char));
1035		if (!margs->principal)
1036			error = ENOMEM;
1037		if (!error)
1038			error = xb_get_bytes(xb, margs->principal, val, 0);
1039	}
1040
1041	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SVCPRINCIPAL)) {
1042		xb_get_32(error, xb, val);
1043		if (!error && ((val < 1) || (val > MAXPATHLEN)))
1044			error=EINVAL;
1045		margs->sprinc = calloc(val+1, sizeof(char));
1046		if (!margs->sprinc)
1047			error = ENOMEM;
1048		if (!error)
1049			error = xb_get_bytes(xb, margs->sprinc, val, 0);
1050	}
1051
1052	if (error)
1053		mountargs_cleanup(margs);
1054	return (error);
1055}
1056
1057/* Map from mount options to printable formats. */
1058struct opt {
1059	int o_opt;
1060	const char *o_name;
1061};
1062static struct opt optnames[] =
1063{
1064	{ MNT_RDONLY,		"ro" },
1065	{ MNT_ASYNC,		"async" },
1066	{ MNT_NODEV,		"nodev" },
1067	{ MNT_NOEXEC,		"noexec" },
1068	{ MNT_NOSUID,		"nosuid" },
1069	{ MNT_SYNCHRONOUS,	"sync" },
1070	{ MNT_UNION,		"union" },
1071	{ MNT_AUTOMOUNTED,	"automounted" },
1072	{ MNT_DEFWRITE, 	"defwrite" },
1073	{ MNT_IGNORE_OWNERSHIP,	"noowners" },
1074	{ MNT_NOATIME,		"noatime" },
1075	{ MNT_QUARANTINE,	"quarantine" },
1076	{ MNT_DONTBROWSE,	"nobrowse" },
1077	{ MNT_CPROTECT,		"protect"},
1078	{ MNT_NOUSERXATTR,	"nouserxattr"},
1079	{ 0, 			NULL }
1080};
1081static struct opt optnames2[] =
1082{
1083	{ MNT_ROOTFS,		"rootfs"},
1084	{ MNT_LOCAL,		"local" },
1085	{ MNT_JOURNALED,	"journaled" },
1086	{ MNT_DOVOLFS,		"dovolfs"},
1087	{ MNT_QUOTA,		"with quotas" },
1088	{ MNT_EXPORTED,		"NFS exported" },
1089	{ MNT_MULTILABEL,	"multilabel"},
1090	{ 0, 			NULL }
1091};
1092
1093static const char *
1094sec_flavor_name(uint32_t flavor)
1095{
1096	switch(flavor) {
1097	case RPCAUTH_NONE:	return ("none");
1098	case RPCAUTH_SYS:	return ("sys");
1099	case RPCAUTH_KRB5:	return ("krb5");
1100	case RPCAUTH_KRB5I:	return ("krb5i");
1101	case RPCAUTH_KRB5P:	return ("krb5p");
1102	default:		return ("?");
1103	}
1104}
1105
1106const char *
1107socket_type(char *sotype)
1108{
1109	if (!strcmp(sotype, "tcp"))
1110		return ("tcp");
1111	if (!strcmp(sotype, "udp"))
1112		return ("udp");
1113	if (!strcmp(sotype, "tcp4"))
1114		return ("proto=tcp");
1115	if (!strcmp(sotype, "udp4"))
1116		return ("proto=udp");
1117	if (!strcmp(sotype, "tcp6"))
1118		return ("proto=tcp6");
1119	if (!strcmp(sotype, "udp6"))
1120		return ("proto=udp6");
1121	if (!strcmp(sotype, "inet4"))
1122		return ("inet4");
1123	if (!strcmp(sotype, "inet6"))
1124		return ("inet6");
1125	if (!strcmp(sotype, "inet"))
1126		return ("inet");
1127	return (sotype);
1128}
1129
1130void
1131print_mountargs(struct mountargs *margs, uint32_t origmargsvers)
1132{
1133	int i, flags;
1134	uint32_t loc, serv, addr, comp;
1135	struct opt *o;
1136	char sep;
1137
1138	/* option separator is space for first option printed and comma for rest */
1139	sep = ' ';
1140	/* call this macro after printing to update separator */
1141#define SEP	sep=','
1142
1143	flags = margs->mntflags;
1144	printf("     General mount flags: 0x%x", flags);
1145	for (o = optnames; flags && o->o_opt; o++)
1146		if (flags & o->o_opt) {
1147			printf("%c%s", sep, o->o_name);
1148			flags &= ~o->o_opt;
1149			SEP;
1150		}
1151	sep = ' ';
1152	for (o = optnames2; flags && o->o_opt; o++)
1153		if (flags & o->o_opt) {
1154			printf("%c%s", sep, o->o_name);
1155			flags &= ~o->o_opt;
1156			SEP;
1157		}
1158	printf("\n");
1159
1160	sep = ' ';
1161	printf("     NFS parameters:");
1162	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_NFS_VERSION)) {
1163		printf("%cvers=%d", sep, margs->nfs_version);
1164		if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_NFS_MINOR_VERSION))
1165			printf(".%d", margs->nfs_minor_version);
1166		SEP;
1167	}
1168	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SOCKET_TYPE))
1169		printf("%c%s", sep, socket_type(margs->sotype)), SEP;
1170	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_NFS_PORT))
1171		printf("%cport=%d", sep, margs->nfs_port), SEP;
1172	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_MOUNT_PORT))
1173		printf("%cmountport=%d", sep, margs->mount_port), SEP;
1174	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_MNTUDP))
1175		printf("%c%smntudp", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_MNTUDP) ? "" : "no"), SEP;
1176	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_SOFT))
1177		printf("%c%s", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_SOFT) ? "soft" : "hard"), SEP;
1178	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_INTR))
1179		printf("%c%sintr", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_INTR) ? "" : "no"), SEP;
1180	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_RESVPORT))
1181		printf("%c%sresvport", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_RESVPORT) ? "" : "no"), SEP;
1182	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_NOCONNECT))
1183		printf("%c%sconn", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_NOCONNECT) ? "no" : ""), SEP;
1184	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_NOCALLBACK))
1185		printf("%c%scallback", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_NOCALLBACK) ? "no" : ""), SEP;
1186	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_NONEGNAMECACHE))
1187		printf("%c%snegnamecache", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_NONEGNAMECACHE) ? "no" : ""), SEP;
1188	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_NONAMEDATTR))
1189		printf("%c%snamedattr", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_NONAMEDATTR) ? "no" : ""), SEP;
1190	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_NOACL))
1191		printf("%c%sacl", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_NOACL) ? "no" : ""), SEP;
1192	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_ACLONLY))
1193		printf("%c%saclonly", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_ACLONLY) ? "" : "no"), SEP;
1194	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_CALLUMNT))
1195		printf("%c%scallumnt", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_CALLUMNT) ? "" : "no"), SEP;
1196	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_LOCK_MODE))
1197		switch(margs->lockmode) {
1198		case NFS_LOCK_MODE_ENABLED:
1199			printf("%clocks", sep);
1200			SEP;
1201			break;
1202		case NFS_LOCK_MODE_DISABLED:
1203			printf("%cnolocks", sep);
1204			SEP;
1205			break;
1206		case NFS_LOCK_MODE_LOCAL:
1207			printf("%clocallocks", sep);
1208			SEP;
1209			break;
1210		}
1211	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_NOQUOTA))
1212		printf("%c%squota", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_NOQUOTA) ? "no" : ""), SEP;
1213	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_READ_SIZE))
1214		printf("%crsize=%d", sep, margs->rsize), SEP;
1215	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_WRITE_SIZE))
1216		printf("%cwsize=%d", sep, margs->wsize), SEP;
1217	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_READAHEAD))
1218		printf("%creadahead=%d", sep, margs->readahead), SEP;
1219	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_READDIR_SIZE))
1220		printf("%cdsize=%d", sep, margs->readdirsize), SEP;
1221	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_RDIRPLUS))
1222		printf("%c%srdirplus", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_RDIRPLUS) ? "" : "no"), SEP;
1223	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_DUMBTIMER))
1224		printf("%c%sdumbtimr", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_DUMBTIMER) ? "" : "no"), SEP;
1225	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_REQUEST_TIMEOUT))
1226		printf("%ctimeo=%ld", sep, ((margs->request_timeout.tv_sec * 10) + (margs->request_timeout.tv_nsec % 100000000))), SEP;
1227	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SOFT_RETRY_COUNT))
1228		printf("%cretrans=%d", sep, margs->soft_retry_count), SEP;
1229	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_MAX_GROUP_LIST))
1230		printf("%cmaxgroups=%d", sep, margs->maxgrouplist), SEP;
1231	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_REG_MIN))
1232		printf("%cacregmin=%ld", sep, margs->acregmin.tv_sec), SEP;
1233	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_REG_MAX))
1234		printf("%cacregmax=%ld", sep, margs->acregmax.tv_sec), SEP;
1235	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN))
1236		printf("%cacdirmin=%ld", sep, margs->acdirmin.tv_sec), SEP;
1237	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX))
1238		printf("%cacdirmax=%ld", sep, margs->acdirmax.tv_sec), SEP;
1239	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_DEAD_TIMEOUT))
1240		printf("%cdeadtimeout=%ld", sep, margs->dead_timeout.tv_sec), SEP;
1241	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_MUTEJUKEBOX))
1242		printf("%c%smutejukebox", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_MUTEJUKEBOX) ? "" : "no"), SEP;
1243	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_EPHEMERAL))
1244		printf("%c%sephemeral", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_EPHEMERAL) ? "" : "no"), SEP;
1245	if (NFS_BITMAP_ISSET(margs->mflags_mask, NFS_MFLAG_NFC))
1246		printf("%c%snfc", sep, NFS_BITMAP_ISSET(margs->mflags, NFS_MFLAG_NFC) ? "" : "no"), SEP;
1247	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SECURITY)) {
1248		printf("%csec=%s", sep, sec_flavor_name(margs->sec.flavors[0]));
1249		for (i=1; i < margs->sec.count; i++)
1250			printf(":%s", sec_flavor_name(margs->sec.flavors[i]));
1251		SEP;
1252	}
1253
1254	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_REALM))
1255		printf("%crealm=%s", sep, margs->realm);
1256	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_PRINCIPAL))
1257		printf("%cprincipal=%s", sep, margs->principal);
1258	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_SVCPRINCIPAL))
1259		printf("%csprincipalm=%s", sep, margs->sprinc);
1260
1261	printf("\n");
1262
1263	if (NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_FS_LOCATIONS)) {
1264		printf("     File system locations:\n");
1265		if (origmargsvers < NFS_ARGSVERSION_XDR) {
1266			printf("       %s", margs->mntfrom ? margs->mntfrom : "???");
1267			if (margs->numlocs && margs->locs[0].servcount &&
1268			    margs->locs[0].servers[0].addrcount &&
1269			    margs->locs[0].servers[0].addrs[0])
1270				printf(" (%s)", margs->locs[0].servers[0].addrs[0]);
1271			printf("\n");
1272		}
1273		if ((origmargsvers == NFS_ARGSVERSION_XDR) || verbose) {
1274			for (loc=0; loc < margs->numlocs; loc++) {
1275				printf("       ");
1276				if (!margs->locs[loc].compcount)
1277					printf("/");
1278				for (comp=0; comp < margs->locs[loc].compcount; comp++)
1279					printf("/%s", margs->locs[loc].components[comp]);
1280				printf(" @");
1281				for (serv=0; serv < margs->locs[loc].servcount; serv++) {
1282					printf(" %s (", margs->locs[loc].servers[serv].name);
1283					for (addr=0; addr < margs->locs[loc].servers[serv].addrcount; addr++)
1284						printf("%s%s", !addr ? "" : ",", margs->locs[loc].servers[serv].addrs[addr]);
1285					printf(")");
1286				}
1287				printf("\n");
1288			}
1289		}
1290	}
1291	if (verbose && NFS_BITMAP_ISSET(margs->mattrs, NFS_MATTR_FH)) {
1292		printf("     fh %d ", margs->fh.fh_len);
1293		for (i=0; i < margs->fh.fh_len; i++)
1294			printf("%02x", margs->fh.fh_data[i] & 0xff);
1295		printf("\n");
1296	}
1297}
1298
1299/*
1300 * Print the given mount info.
1301 */
1302void
1303print_mountinfo(struct statfs *mnt, char *buf, uint buflen)
1304{
1305	struct xdrbuf xb;
1306	uint32_t val, miattrs[NFS_MIATTR_BITMAP_LEN], miflags[NFS_MIFLAG_BITMAP_LEN];
1307	int error = 0, len;
1308	struct mountargs origargs, curargs;
1309	uint32_t flags, loc, serv, addr, comp;
1310
1311	NFS_BITMAP_ZERO(miattrs, NFS_MIATTR_BITMAP_LEN);
1312	NFS_BITMAP_ZERO(miflags, NFS_MIFLAG_BITMAP_LEN);
1313	bzero(&origargs, sizeof(origargs));
1314	bzero(&curargs, sizeof(curargs));
1315
1316	xb_init_buffer(&xb, buf, buflen);
1317	xb_get_32(error, &xb, val);			/* NFS_MOUNT_INFO_VERSION */
1318	if (error)
1319		goto out;
1320	if (val != NFS_MOUNT_INFO_VERSION) {
1321		printf("%s unknown mount info version %d\n\n", mnt->f_mntonname, val);
1322		return;
1323	}
1324	xb_get_32(error, &xb, val);			/* mount info length */
1325	if (error)
1326		goto out;
1327	if (val > buflen) {
1328		printf("%s bogus mount info length %d > %d\n\n", mnt->f_mntonname, val, buflen);
1329		return;
1330	}
1331	len = NFS_MIATTR_BITMAP_LEN;
1332	xb_get_bitmap(error, &xb, miattrs, len);
1333	if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_FLAGS)) {
1334		len = NFS_MIFLAG_BITMAP_LEN;
1335		xb_get_bitmap(error, &xb, miflags, len);
1336	}
1337	if (error)
1338		goto out;
1339
1340	printf("%s from %s\n", mnt->f_mntonname, mnt->f_mntfromname);
1341
1342	if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_ORIG_ARGS)) {
1343		xb_get_32(error, &xb, val);			/* original mount args length */
1344		if (!error)
1345			error = parse_mountargs(&xb, val, &origargs);
1346	}
1347	if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_CUR_ARGS)) {
1348		xb_get_32(error, &xb, val);			/* current mount args length */
1349		if (!error)
1350			error = parse_mountargs(&xb, val, &curargs);
1351	}
1352	if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_CUR_LOC_INDEX)) {
1353		xb_get_32(error, &xb, flags);
1354		xb_get_32(error, &xb, loc);
1355		xb_get_32(error, &xb, serv);
1356		xb_get_32(error, &xb, addr);
1357	}
1358	if (error)
1359		goto out;
1360
1361	if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_ORIG_ARGS)) {
1362		printf("  -- Original mount options:\n");
1363		print_mountargs(&origargs, origargs.margsvers);
1364	}
1365	if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_CUR_ARGS)) {
1366		printf("  -- Current mount parameters:\n");
1367		print_mountargs(&curargs, origargs.margsvers);
1368		if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_CUR_LOC_INDEX) &&
1369		    (verbose || (curargs.numlocs > 1) || (curargs.locs[0].servcount > 1) ||
1370		     (curargs.locs[0].servers[0].addrcount > 1))) {
1371			printf("     Current location: 0x%x %d %d %d: ", flags, loc, serv, addr);
1372			if ((loc >= curargs.numlocs) || (serv >= curargs.locs[loc].servcount) ||
1373			    (addr >= curargs.locs[loc].servers[serv].addrcount)) {
1374				printf("<invalid>\n");
1375			} else {
1376				printf("\n       ");
1377				if (!curargs.locs[loc].compcount)
1378					printf("/");
1379				for (comp=0; comp < curargs.locs[loc].compcount; comp++)
1380					printf("/%s", curargs.locs[loc].components[comp]);
1381				printf(" @ %s (%s)\n", curargs.locs[loc].servers[serv].name,
1382					curargs.locs[loc].servers[serv].addrs[addr]);
1383			}
1384		}
1385	}
1386	if (NFS_BITMAP_ISSET(miattrs, NFS_MIATTR_FLAGS)) {
1387		printf("     Status flags: 0x%x", miflags[0]);
1388		if (NFS_BITMAP_ISSET(miflags, NFS_MIFLAG_DEAD))
1389			printf(",dead");
1390		if (NFS_BITMAP_ISSET(miflags, NFS_MIFLAG_NOTRESP))
1391			printf(",not responding");
1392		if (NFS_BITMAP_ISSET(miflags, NFS_MIFLAG_RECOVERY))
1393			printf(",recovery");
1394		printf("\n");
1395	}
1396out:
1397	if (error)
1398		printf("%s error parsing mount info (%d)\n", mnt->f_mntonname, error);
1399	printf("\n");
1400	mountargs_cleanup(&origargs);
1401	mountargs_cleanup(&curargs);
1402}
1403
1404/*
1405 * Print mount info for given mount (or all NFS mounts)
1406 */
1407void
1408do_mountinfo(char *mountpath)
1409{
1410	struct statfs *mntbuf;
1411	int i, mntsize;
1412	char *buf = NULL, *p;
1413	uint buflen = 0;
1414
1415	if (mountpath) {
1416		/* be nice and strip any trailing slashes */
1417		p = mountpath + strlen(mountpath) - 1;
1418		while ((p > mountpath) && (*p == '/'))
1419			*p-- = '\0';
1420	}
1421
1422	if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
1423		err(1, "getmntinfo");
1424	for (i = 0; i < mntsize; i++) {
1425		/* check if this mount is one we want */
1426		if (mountpath && (strcmp(mountpath, mntbuf[i].f_mntonname) || !strcmp(mntbuf[i].f_fstypename, "autofs")))
1427			continue;
1428		if (!mountpath && strcmp(mntbuf[i].f_fstypename, "nfs"))
1429			continue;
1430		/* Get the mount information. */
1431		if (read_mountinfo(&mntbuf[i].f_fsid, &buf, &buflen)) {
1432			warnx("Unable to get mount info for %s", mntbuf[i].f_mntonname);
1433			continue;
1434		}
1435		/* Print the mount information. */
1436		print_mountinfo(&mntbuf[i], buf, buflen);
1437	}
1438
1439	/* clean up */
1440	if (buf)
1441		free(buf);
1442}
1443
1444
1445
1446/* ************************ */
1447/* *** Per-export Stats *** */
1448/* ************************ */
1449
1450/*
1451 * Print the stats of all nfs exported directories
1452 */
1453void
1454do_exports_normal(void)
1455{
1456	struct nfs_export_stat_desc *stat_desc;
1457	struct nfs_export_stat_rec  *rec;
1458	char  *buf;
1459	uint  bufLen, i, recs;
1460
1461	/* Read in export stats from the kernel */
1462	buf = NULL;
1463
1464	/* check for failure  */
1465	if (read_export_stats(&buf, &bufLen))
1466		return;
1467
1468	/* check for empty export table */
1469	stat_desc = (struct nfs_export_stat_desc *)buf;
1470	recs = stat_desc->rec_count;
1471	if (!recs) {
1472		printf("No export stat data found\n");
1473		free(buf);
1474		return;
1475	}
1476
1477	/* init record pointer to position following stat descriptor */
1478	rec = (struct nfs_export_stat_rec *)(buf + sizeof(struct nfs_export_stat_desc));
1479
1480	/* print out a header */
1481	printf("Exported Directory Info:\n");
1482	printf("%12s  %12s  %12s\n", "Requests", "Read Bytes", "Write Bytes");
1483
1484	/* loop through, printing out stats of each export */
1485	for(i = 0; i < recs; i++)
1486		printf("%12llu  %12llu  %12llu  %s\n", rec[i].ops,
1487		rec[i].bytes_read, rec[i].bytes_written, rec[i].path);
1488
1489	/* clean up */
1490	free(buf);
1491}
1492
1493/*
1494 * Print a running summary of nfs export statistics.
1495 * Repeat display every interval seconds, showing statistics
1496 * collected over that interval.  Assumes that interval is non-zero.
1497 * First line printed at top of screen is always cumulative.
1498 */
1499void
1500do_exports_interval(u_int interval)
1501{
1502	char *oldExportBuf, *newExportBuf, *tmpBuf;
1503	uint oldLen, newLen, tmpLen;
1504	int hdrcnt;
1505	sigset_t sigset, oldsigset;
1506
1507	oldExportBuf = newExportBuf = NULL;
1508	oldLen = newLen = 0;
1509
1510	signal(SIGALRM, catchalarm);
1511	signalled = 0;
1512	alarm(interval);
1513
1514	for (hdrcnt = 1;;) {
1515		if (!--hdrcnt) {
1516			printf("%12s  %12s  %12s\n", "Requests", "Read Bytes", "Write Bytes");
1517			fflush(stdout);
1518			hdrcnt = 20;
1519		}
1520
1521		if (read_export_stats(&newExportBuf, &newLen) == 0) {
1522			display_export_diffs(newExportBuf, oldExportBuf);
1523			tmpBuf = oldExportBuf;
1524			tmpLen = oldLen;
1525			oldExportBuf = newExportBuf;
1526			oldLen = newLen;
1527			newExportBuf = tmpBuf;
1528			newLen = tmpLen;
1529		}
1530
1531		fflush(stdout);
1532		sigemptyset(&sigset);
1533		sigaddset(&sigset, SIGALRM);
1534		if (sigprocmask(SIG_BLOCK, &sigset, &oldsigset) == -1)
1535			err(1, "sigprocmask failed");
1536		if (!signalled) {
1537			sigemptyset(&sigset);
1538			sigsuspend(&sigset);
1539		}
1540		if (sigprocmask(SIG_SETMASK, &oldsigset, NULL) == -1)
1541			err(1, "sigprocmask failed");
1542		signalled = 0;
1543		alarm(interval);
1544	}
1545}
1546
1547void
1548display_export_diffs(char *newb, char *oldb)
1549{
1550	struct nfs_export_stat_desc *hdr;
1551	struct nfs_export_stat_rec  *rec, *oldRec;
1552	uint i, recs;
1553
1554	if (newb == NULL)
1555		return;
1556
1557	/* Determine how many records in newb */
1558	hdr = (struct nfs_export_stat_desc *)newb;
1559	recs = hdr->rec_count;
1560
1561	/* check for empty export table */
1562	if (!recs) {
1563		printf("No exported directories found\n");
1564		return;
1565	}
1566
1567	/* initialize rec pointer */
1568	rec = (struct nfs_export_stat_rec *)(newb + sizeof(struct nfs_export_stat_desc));
1569
1570	for(i = 0; i < recs; i++) {
1571		/* find old export record for this path */
1572		oldRec = findExport(rec[i].path, oldb);
1573		if (oldRec != NULL) {
1574			printf("%12llu %12llu %12llu %s\n",
1575			(rec[i].ops >= oldRec->ops) ?
1576				rec[i].ops - oldRec->ops : oldRec->ops - rec[i].ops,
1577			(rec[i].bytes_read >= oldRec->bytes_read) ?
1578				rec[i].bytes_read - oldRec->bytes_read : oldRec->bytes_read - rec[i].bytes_read,
1579			(rec[i].bytes_written >= oldRec->bytes_written) ?
1580				rec[i].bytes_written - oldRec->bytes_written : oldRec->bytes_written - rec[i].bytes_written,
1581			rec[i].path);
1582		}
1583		else
1584			printf("%12llu %12llu %12llu %s\n",
1585				rec[i].ops, rec[i].bytes_read, rec[i].bytes_written, rec[i].path);
1586	}
1587}
1588
1589struct nfs_export_stat_rec *
1590findExport(char *path, char *buf)
1591{
1592	struct nfs_export_stat_desc *hdr;
1593	struct nfs_export_stat_rec  *rec, *retRec;
1594	uint    i, recs;
1595
1596	retRec = NULL;
1597
1598	if (buf == NULL)
1599		return retRec;
1600
1601	/* determine how many records in buf */
1602	hdr = (struct nfs_export_stat_desc *)buf;
1603	recs = hdr->rec_count;
1604
1605	/* check if no records to compare */
1606	if (!recs)
1607		return retRec;
1608
1609	/* initialize our rec pointer */
1610	rec = (struct nfs_export_stat_rec *)(buf + sizeof(struct nfs_export_stat_desc));
1611
1612	for(i = 0; i < recs; i++) {
1613		if (strcmp(path, rec[i].path) == 0) {
1614			/* found a match */
1615			retRec = &rec[i];
1616			break;
1617		}
1618	}
1619
1620	return retRec;
1621}
1622
1623
1624/* ****************** */
1625/* *** User Stats *** */
1626/* ****************** */
1627
1628/*
1629 * Print active user stats for each nfs exported directory
1630 */
1631void
1632do_active_users_normal(u_int flags)
1633{
1634	struct nfs_user_stat_desc	*stat_desc;
1635	struct nfs_export_node_head	*export_list;
1636	struct nfs_export_node		*export_node;
1637	struct nfs_active_user_node	*unode;
1638	char				*buf;
1639	uint				bufLen, recs;
1640
1641	/* Read in user stats from the kernel */
1642	buf = NULL;
1643	if (read_active_user_stats(&buf, &bufLen))
1644		return;
1645
1646	/* check for empty user list */
1647	stat_desc = (struct nfs_user_stat_desc *)buf;
1648	recs = stat_desc->rec_count;
1649	if (!recs) {
1650		printf("No NFS active user statistics found.\n");
1651		free(buf);
1652		return;
1653	}
1654
1655	/* get a sorted list */
1656	export_list = get_sorted_active_user_list(buf);
1657	if (!export_list) {
1658		printf("Not enough  memory for displaying active user statistics\n");
1659		free(buf);
1660		return;
1661	}
1662
1663	/* print out a header */
1664	printf("NFS Active User Info:\n");
1665
1666	LIST_FOREACH(export_node, export_list, export_next) {
1667		printf("%s\n", export_node->rec->path);
1668		printf("%12s  %12s  %12s  %-7s  %-8s %s\n",
1669		    "Requests", "Read Bytes", "Write Bytes",
1670		    "Idle", "User", "IP Address");
1671
1672		LIST_FOREACH(unode, &export_node->nodes, user_next)
1673			displayActiveUserRec(unode->rec, flags);
1674	}
1675
1676	/* clean up */
1677	free_nfs_export_list(export_list);
1678	free(export_list);
1679	free(buf);
1680}
1681
1682/*
1683 * Print a running summary of nfs active user statistics.
1684 * Repeat display every interval seconds, showing statistics
1685 * collected over that interval.  Assumes that interval is non-zero.
1686 * First line printed at top of screen is always cumulative.
1687 */
1688void
1689do_active_users_interval(u_int interval, u_int flags)
1690{
1691	char		*oldBuf, *newBuf, *tmpBuf;
1692	uint		oldLen, newLen, tmpLen;
1693	int		hdrcnt;
1694	sigset_t	sigset, oldsigset;
1695
1696	oldBuf = newBuf = NULL;
1697	oldLen = newLen = 0;
1698
1699	signal(SIGALRM, catchalarm);
1700	signalled = 0;
1701	alarm(interval);
1702
1703	for (hdrcnt = 1;;) {
1704		if (!--hdrcnt) {
1705			printf("%12s  %12s  %12s  %-7s  %-8s %s\n",
1706			    "Requests", "Read Bytes", "Write Bytes",
1707			    "Idle", "User", "IP Address");
1708			fflush(stdout);
1709			hdrcnt = 20;
1710		}
1711
1712		if (read_active_user_stats(&newBuf, &newLen) == 0) {
1713			display_active_user_diffs(newBuf, oldBuf, flags);
1714			tmpBuf = oldBuf;
1715			tmpLen = oldLen;
1716			oldBuf = newBuf;
1717			oldLen = newLen;
1718			newBuf = tmpBuf;
1719			newLen = tmpLen;
1720		}
1721
1722		fflush(stdout);
1723		sigemptyset(&sigset);
1724		sigaddset(&sigset, SIGALRM);
1725		if (sigprocmask(SIG_BLOCK, &sigset, &oldsigset) == -1)
1726			err(1, "sigprocmask failed");
1727		if (!signalled) {
1728			sigemptyset(&sigset);
1729			sigsuspend(&sigset);
1730		}
1731		if (sigprocmask(SIG_SETMASK, &oldsigset, NULL) == -1)
1732			err(1, "sigprocmask failed");
1733		signalled = 0;
1734		alarm(interval);
1735	}
1736}
1737
1738void
1739display_active_user_diffs(char *newb, char *oldb, u_int flags)
1740{
1741	struct nfs_user_stat_desc	*stat_desc;
1742	struct nfs_export_node_head	*export_list;
1743	struct nfs_export_node		*export_node;
1744	struct nfs_active_user_node	*unode;
1745
1746	struct nfs_user_stat_user_rec	*rec, *oldrec, diffrec;
1747	struct nfs_user_stat_path_rec	*pathrec;
1748
1749	if (newb == NULL)
1750		return;
1751
1752	/* check for empty export table */
1753	stat_desc = (struct nfs_user_stat_desc *)newb;
1754	if (!stat_desc->rec_count) {
1755		printf("No NFS active user statistics found.\n");
1756		return;
1757	}
1758
1759	/* get a sorted list from newb */
1760	export_list = get_sorted_active_user_list(newb);
1761	if (!export_list) {
1762		printf("Not enough  memory for displaying active user statistics\n");
1763		return;
1764	}
1765
1766	LIST_FOREACH(export_node, export_list, export_next) {
1767		pathrec = export_node->rec;
1768		printf("%s\n", export_node->rec->path);
1769
1770		LIST_FOREACH(unode, &export_node->nodes, user_next) {
1771			rec = unode->rec;
1772			/* check for old record */
1773			oldrec = findActiveUser(pathrec->path, rec, oldb);
1774
1775			if (oldrec != NULL) {
1776				/* setup diff rec */
1777				diffrec.uid = rec->uid;
1778				diffrec.tm_start = rec->tm_start;
1779				diffrec.tm_last = rec->tm_last;
1780				diffrec.ops = serialdiff_64(rec->ops, oldrec->ops);
1781				diffrec.bytes_read = serialdiff_64(rec->bytes_read, oldrec->bytes_read);
1782				diffrec.bytes_written = serialdiff_64(rec->bytes_written, oldrec->bytes_written);
1783				bcopy(&rec->sock, &diffrec.sock, rec->sock.ss_len);
1784
1785				/* display differential record */
1786				displayActiveUserRec(&diffrec, flags);
1787			}
1788			else
1789				displayActiveUserRec(rec, flags);
1790		}
1791	}
1792
1793	/* clean up */
1794	free_nfs_export_list(export_list);
1795	free(export_list);
1796}
1797
1798struct nfs_user_stat_user_rec *
1799findActiveUser(char *path, struct nfs_user_stat_user_rec *rec, char *buf)
1800{
1801	struct nfs_user_stat_desc	*stat_desc;
1802	struct nfs_user_stat_user_rec	*tmpRec, *retRec;
1803	struct nfs_user_stat_path_rec	*pathrec;
1804	char				*bufp;
1805	uint				i, recs, scan_state;
1806
1807#define FIND_EXPORT 0
1808#define FIND_USER 1
1809
1810	scan_state = FIND_EXPORT;
1811
1812	retRec = NULL;
1813
1814	if (buf == NULL)
1815		return retRec;
1816
1817	/* determine how many records in buf */
1818	stat_desc = (struct nfs_user_stat_desc *)buf;
1819	recs = stat_desc->rec_count;
1820
1821	/* check if no records to compare */
1822	if (!recs)
1823		return retRec;
1824
1825	/* initialize buf pointer */
1826	bufp = buf + sizeof(struct nfs_user_stat_desc);
1827
1828	for(i = 0; i < recs; i++) {
1829		switch(*bufp) {
1830			case NFS_USER_STAT_PATH_REC:
1831				if(scan_state == FIND_EXPORT) {
1832					pathrec = (struct nfs_user_stat_path_rec *)bufp;
1833					if(!strcmp(path, pathrec->path))
1834						scan_state = FIND_USER;
1835				}
1836				else {
1837					/* encountered the next export, didn't find user. */
1838					goto done;
1839				}
1840				bufp += sizeof(struct nfs_user_stat_path_rec);
1841				break;
1842			case NFS_USER_STAT_USER_REC:
1843				if(scan_state == FIND_USER) {
1844					tmpRec = (struct nfs_user_stat_user_rec *)bufp;
1845					if (!cmp_active_user(rec, tmpRec)) {
1846						/* found a match */
1847						retRec = tmpRec;
1848						goto done;
1849					}
1850				}
1851				bufp += sizeof(struct nfs_user_stat_user_rec);
1852				break;
1853			default:
1854				goto done;
1855		}
1856	}
1857
1858done:
1859	return retRec;
1860}
1861
1862void
1863displayActiveUserRec(struct nfs_user_stat_user_rec *rec, u_int flags)
1864{
1865	struct sockaddr_in	*in;
1866	struct sockaddr_in6	*in6;
1867	struct hostent		*hp = NULL;
1868	struct passwd		*pw;
1869	struct timeval		now;
1870	struct timezone		tz;
1871	uint32_t		now32, hr, min, sec;
1872	char			addrbuf[NI_MAXHOST];
1873	char			unknown[] = "* * * *";
1874	char			*addr = unknown;
1875
1876	/* get current time for calculating idle time */
1877	gettimeofday(&now, &tz);
1878
1879	/* calculate idle hour, min sec */
1880	now32 = (uint32_t)now.tv_sec;
1881	if (now32 >= rec->tm_last)
1882		sec = now32 - rec->tm_last;
1883	else
1884		sec = ~(rec->tm_last - now32) + 1;
1885	hr = sec / 3600;
1886	sec %= 3600;
1887	min = sec / 60;
1888	sec %= 60;
1889
1890	/* setup ip address string */
1891	if (rec->sock.ss_family == AF_INET) {
1892		/* ipv4 */
1893		in = (struct sockaddr_in *)&rec->sock;
1894		if (!(flags & NUMERIC_NET))
1895			hp = gethostbyaddr((char *)&in->sin_addr, sizeof(in->sin_addr), AF_INET);
1896		if (hp && hp->h_name)
1897			addr = hp->h_name;
1898		else if (inet_ntop(AF_INET, &in->sin_addr, addrbuf, sizeof(addrbuf)))
1899			addr = addrbuf;
1900	} else if (rec->sock.ss_family == AF_INET6) {
1901		/* ipv6 */
1902		in6 = (struct sockaddr_in6 *)&rec->sock;
1903		if (!(flags & NUMERIC_NET))
1904			hp = gethostbyaddr((char *)&in6->sin6_addr, sizeof(in6->sin6_addr), AF_INET6);
1905		if (hp && hp->h_name)
1906			addr = hp->h_name;
1907		else if (inet_ntop(AF_INET6, &in6->sin6_addr, addrbuf, sizeof(addrbuf)))
1908			addr = addrbuf;
1909	}
1910
1911	if ((flags & NUMERIC_USER) || !(pw = getpwuid(rec->uid))) {
1912		/* print uid */
1913		printf("%12llu  %12llu  %12llu  %1u:%02u:%02u  %-8u %s\n",
1914		    rec->ops, rec->bytes_read, rec->bytes_written,
1915		    hr, min, sec, rec->uid, addr);
1916	} else {
1917		/* print user name */
1918		printf("%12llu  %12llu  %12llu  %1u:%02u:%02u  %-8.8s %s\n",
1919		    rec->ops, rec->bytes_read, rec->bytes_written,
1920		    hr, min, sec, pw->pw_name, addr);
1921	}
1922}
1923
1924/* Returns zero if both uid and IP address fields match */
1925int
1926cmp_active_user(struct nfs_user_stat_user_rec *rec1, struct nfs_user_stat_user_rec *rec2)
1927{
1928	struct sockaddr_in	*ipv4_sock1, *ipv4_sock2;
1929	struct sockaddr_in6	*ipv6_sock1, *ipv6_sock2;
1930	int			retVal = 1;
1931
1932	/* check uid */
1933	if (rec1->uid != rec2->uid)
1934		return retVal;
1935
1936	/* check address length */
1937	if (rec1->sock.ss_len != rec2->sock.ss_len)
1938		return retVal;
1939
1940	/* Check address family */
1941	if (rec1->sock.ss_family != rec2->sock.ss_family)
1942		return retVal;
1943
1944	if (rec1->sock.ss_family == AF_INET) {
1945		/* IPv4 */
1946		ipv4_sock1 = (struct sockaddr_in *)&rec1->sock;
1947		ipv4_sock2 = (struct sockaddr_in *)&rec2->sock;
1948
1949		if (!bcmp(&ipv4_sock1->sin_addr, &ipv4_sock2->sin_addr, sizeof(struct in_addr)))
1950			retVal = 0;
1951
1952	}
1953	else {
1954		/* IPv6 */
1955		ipv6_sock1 = (struct sockaddr_in6 *)&rec1->sock;
1956		ipv6_sock2 = (struct sockaddr_in6 *)&rec2->sock;
1957
1958		if (!bcmp(&ipv6_sock1->sin6_addr, &ipv6_sock2->sin6_addr, sizeof(struct in6_addr)))
1959			retVal = 0;
1960	}
1961
1962	return retVal;
1963}
1964
1965struct nfs_export_node_head *
1966get_sorted_active_user_list(char *buf)
1967{
1968	struct nfs_user_stat_desc	*stat_desc;
1969	struct nfs_export_node_head	*export_list;
1970	struct nfs_export_node		*export_node;
1971	struct nfs_active_user_node	*unode, *unode_before, *unode_after;
1972	char				*bufp;
1973	uint				i, recs, err;
1974
1975	/* first check for empty user list */
1976	stat_desc = (struct nfs_user_stat_desc *)buf;
1977	recs = stat_desc->rec_count;
1978	if (!recs)
1979		return NULL;
1980
1981	export_list = (struct nfs_export_node_head *)malloc(sizeof(struct nfs_export_node_head));
1982	if (export_list == NULL)
1983		return NULL;
1984	LIST_INIT(export_list);
1985		export_node = NULL;
1986	err = 0;
1987
1988	/* init record pointer to position following the stat descriptor */
1989	bufp = buf + sizeof(struct nfs_user_stat_desc);
1990
1991	/* loop through, printing out each record */
1992	for(i = 0; i < recs; i++) {
1993		switch(*bufp) {
1994			case NFS_USER_STAT_PATH_REC:
1995				/* create a new export node */
1996				export_node  = (struct nfs_export_node *)malloc(sizeof(struct nfs_export_node));
1997				if (export_node == NULL) {
1998					err = 1;
1999					goto done_err;
2000				}
2001				LIST_INIT(&export_node->nodes);
2002				export_node->rec = (struct nfs_user_stat_path_rec *)bufp;
2003				LIST_INSERT_HEAD(export_list, export_node, export_next);
2004
2005				bufp += sizeof(struct nfs_user_stat_path_rec);
2006				break;
2007			case NFS_USER_STAT_USER_REC:
2008				if (export_node == NULL) {
2009					err = 1;
2010					goto done_err;
2011				}
2012				/* create a new user node */
2013				unode = (struct nfs_active_user_node *)malloc(sizeof(struct nfs_active_user_node));
2014				if (unode == NULL) {
2015					err = 1;
2016					goto done_err;
2017				}
2018				unode->rec = (struct nfs_user_stat_user_rec *)bufp;
2019
2020				/* insert in decending order */
2021				unode_before = NULL;
2022				LIST_FOREACH(unode_after, &export_node->nodes, user_next) {
2023					if (unode->rec->tm_last > unode_after->rec->tm_last)
2024						break;
2025					unode_before = unode_after;
2026				}
2027				if (unode_after)
2028					LIST_INSERT_BEFORE(unode_after, unode, user_next);
2029				else if (unode_before)
2030					LIST_INSERT_AFTER(unode_before, unode, user_next);
2031				else
2032					LIST_INSERT_HEAD(&export_node->nodes, unode, user_next);
2033
2034				bufp += sizeof(struct nfs_user_stat_user_rec);
2035				break;
2036
2037			default:
2038				printf("nfsstat: unexpected record type 0x%02x in active user data stream\n", *bufp);
2039				err = 1;
2040				goto done_err;
2041		}
2042	}
2043
2044done_err:
2045	if (err) {
2046		free_nfs_export_list(export_list);
2047		free(export_list);
2048		export_list = NULL;
2049	}
2050	return(export_list);
2051}
2052
2053void
2054free_nfs_export_list(struct nfs_export_node_head *export_list)
2055{
2056	struct nfs_export_node		*exp_node;
2057	struct nfs_active_user_node	*unode;
2058
2059	while ((exp_node = LIST_FIRST(export_list))) {
2060		LIST_REMOVE(exp_node, export_next);
2061		while((unode = LIST_FIRST(&exp_node->nodes))) {
2062			LIST_REMOVE(unode, user_next);
2063			free(unode);
2064		}
2065		free(exp_node);
2066	}
2067}
2068
2069uint64_t
2070serialdiff_64(uint64_t new, uint64_t old)
2071{
2072        if(new > old)
2073                return (new - old);
2074        else
2075                return ( ~(old - new) +  1);
2076}
2077
2078
2079void
2080printhdr(void)
2081{
2082	printf("        %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s\n",
2083	    "Getattr", "Lookup", "Readlink", "Read", "Write", "Rename",
2084	    "Access", "Readdir");
2085	fflush(stdout);
2086}
2087
2088/*
2089 * Called if an interval expires before sidewaysintpr has completed a loop.
2090 * Sets a flag to not wait for the alarm.
2091 */
2092void
2093catchalarm(__unused int dummy)
2094{
2095	signalled = 1;
2096}
2097
2098void
2099usage(void)
2100{
2101	fprintf(stderr, "usage: nfsstat [-cseuv] [-w interval] [-n user|net]\n");
2102	exit(1);
2103}
2104