]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsstat/nfsstat.c
Keep the interval output quite when there is no NFS traffic.
[nfs-utils.git] / utils / nfsstat / nfsstat.c
1 /*
2  * nfsstat.c            Output NFS statistics
3  *
4  * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #define NFSSRVSTAT      "/proc/net/rpc/nfsd"
12 #define NFSCLTSTAT      "/proc/net/rpc/nfs"
13
14 #define MOUNTSFILE      "/proc/mounts"
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <getopt.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <signal.h>
24 #include <time.h>
25
26 #define MAXNRVALS       32
27
28 static unsigned int     srvproc2info[20], srvproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */
29 static unsigned int     cltproc2info[20], cltproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */
30 static unsigned int     srvproc3info[24], srvproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
31 static unsigned int     cltproc3info[24], cltproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
32 static unsigned int     srvproc4info[4], srvproc4info_old[4];   /* NFSv4 call counts ([0] == 2) */
33 static unsigned int     cltproc4info[37], cltproc4info_old[37]; /* NFSv4 call counts ([0] == 35) */
34 static unsigned int     srvproc4opsinfo[42], srvproc4opsinfo_old[42];   /* NFSv4 call counts ([0] == 40) */
35 static unsigned int     srvnetinfo[5], srvnetinfo_old[5];       /* 0  # of received packets
36                                                                  * 1  UDP packets
37                                                                  * 2  TCP packets
38                                                                  * 3  TCP connections
39                                                                  */
40 static unsigned int     cltnetinfo[5], cltnetinfo_old[5];       /* 0  # of received packets
41                                                                  * 1  UDP packets
42                                                                  * 2  TCP packets
43                                                                  * 3  TCP connections
44                                                                  */
45
46 static unsigned int     srvrpcinfo[6], srvrpcinfo_old[6];       /* 0  total # of RPC calls
47                                                                  * 1  total # of bad calls
48                                                                  * 2  bad format
49                                                                  * 3  authentication failed
50                                                                  * 4  unknown client
51                                                                  */
52 static unsigned int     cltrpcinfo[4], cltrpcinfo_old[4];       /* 0  total # of RPC calls
53                                                                  * 1  retransmitted calls
54                                                                  * 2  cred refreshs
55                                                                  */
56
57 static unsigned int     srvrcinfo[9], srvrcinfo_old[9];         /* 0  repcache hits
58                                                                  * 1  repcache hits
59                                                                  * 2  uncached reqs
60                                                                  * (for pre-2.4 kernels:)
61                                                                  * 3  FH lookups
62                                                                  * 4  'anon' FHs
63                                                                  * 5  noncached non-directories
64                                                                  * 6  noncached directories
65                                                                  * 7  stale
66                                                                  */
67
68 static unsigned int     srvfhinfo[7], srvfhinfo_old[7];         /* (for kernels >= 2.4.0)
69                                                                  * 0  stale
70                                                                  * 1  FH lookups
71                                                                  * 2  'anon' FHs
72                                                                  * 3  noncached directories
73                                                                  * 4  noncached non-directories
74                                                                  * leave hole to relocate stale for order
75                                                                  *    compatability.
76                                                                  */
77
78 static const char *     nfsv2name[18] = {
79         "null", "getattr", "setattr", "root",   "lookup",  "readlink",
80         "read", "wrcache", "write",   "create", "remove",  "rename",
81         "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"
82 };
83
84 static const char *     nfsv3name[22] = {
85         "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
86         "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
87         "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
88         "fsstat", "fsinfo",  "pathconf", "commit"
89 };
90
91 static const char *     nfssrvproc4name[2] = {
92         "null",
93         "compound",
94 };
95
96 static const char *     nfscltproc4name[35] = {
97         "null",      "read",      "write",   "commit",      "open",        "open_conf",
98         "open_noat", "open_dgrd", "close",   "setattr",     "fsinfo",      "renew",
99         "setclntid", "confirm",   "lock",
100         "lockt",     "locku",     "access",  "getattr",     "lookup",      "lookup_root",
101         "remove",    "rename",    "link",    "symlink",     "create",      "pathconf",
102         "statfs",    "readlink",  "readdir", "server_caps", "delegreturn", "getacl",
103         "setacl",    "fs_locations"
104 };
105
106 static const char *     nfssrvproc4opname[40] = {
107         "op0-unused",   "op1-unused", "op2-future",  "access",     "close",       "commit",
108         "create",       "delegpurge", "delegreturn", "getattr",    "getfh",       "link",
109         "lock",         "lockt",      "locku",       "lookup",     "lookup_root", "nverify",
110         "open",         "openattr",   "open_conf",   "open_dgrd",  "putfh",       "putpubfh",
111         "putrootfh",    "read",       "readdir",     "readlink",   "remove",      "rename",
112         "renew",        "restorefh",  "savefh",      "secinfo",    "setattr",     "setcltid",
113         "setcltidconf", "verify",     "write",       "rellockowner"
114 };
115
116 #define LABEL_srvnet            "Server packet stats:\n"
117 #define LABEL_srvrpc            "Server rpc stats:\n"
118 #define LABEL_srvrc             "Server reply cache:\n"
119 #define LABEL_srvfh             "Server file handle cache:\n"
120 #define LABEL_srvproc2          "Server nfs v2:\n"
121 #define LABEL_srvproc3          "Server nfs v3:\n"
122 #define LABEL_srvproc4          "Server nfs v4:\n"
123 #define LABEL_srvproc4ops       "Server nfs v4 operations:\n"
124 #define LABEL_cltnet            "Client packet stats:\n"
125 #define LABEL_cltrpc            "Client rpc stats:\n"
126 #define LABEL_cltproc2          "Client nfs v2:\n"
127 #define LABEL_cltproc3          "Client nfs v3:\n"
128 #define LABEL_cltproc4          "Client nfs v4:\n"
129
130 typedef struct statinfo {
131         char            *tag;
132         char            *label;
133         int             nrvals;
134         unsigned int *  valptr;
135 } statinfo;
136
137 /*
138  * We now build the arrays of statinfos using macros, which will make it easier
139  * to add new variables for --sleep.  e.g., SRV(net) expands into the struct
140  * statinfo:  { "net", "Server packet stats:\n", 5, srvnetinfo }
141  */
142 #define ARRAYSIZE(x)            sizeof(x)/sizeof(*x)
143 #define STATINFO(k, t, s...)    { #t, LABEL_##k##t, ARRAYSIZE(k##t##info##s), k##t##info##s }
144 #define SRV(t, s...)            STATINFO(srv, t, s)
145 #define CLT(t, s...)            STATINFO(clt, t, s)
146 #define DECLARE_SRV(n, s...)    static statinfo n##s[] = { \
147                                         SRV(net,s), \
148                                         SRV(rpc,s), \
149                                         SRV(rc,s), \
150                                         SRV(fh,s), \
151                                         SRV(proc2,s), \
152                                         SRV(proc3,s),\
153                                         SRV(proc4,s), \
154                                         SRV(proc4ops,s),\
155                                         { NULL, NULL, 0, NULL }\
156                                 }
157 #define DECLARE_CLT(n, s...)    static statinfo n##s[] = { \
158                                         CLT(net,s), \
159                                         CLT(rpc,s), \
160                                         CLT(proc2,s),\
161                                         CLT(proc3,s), \
162                                         CLT(proc4,s),\
163                                         { NULL, NULL, 0, NULL }\
164                                 }
165 DECLARE_SRV(srvinfo);
166 DECLARE_SRV(srvinfo, _old);
167 DECLARE_CLT(cltinfo);
168 DECLARE_CLT(cltinfo, _old);
169
170 static void             print_all_stats(int, int, int);
171 static void             print_server_stats(int, int);
172 static void             print_client_stats(int, int);
173 static void             print_numbers(const char *, unsigned int *,
174                                         unsigned int);
175 static void             print_callstats(const char *, const char **,
176                                         unsigned int *, unsigned int);
177 static int              parse_raw_statfile(const char *, struct statinfo *);
178 static int              parse_pretty_statfile(const char *, struct statinfo *);
179
180 static statinfo         *get_stat_info(const char *, struct statinfo *);
181
182 static int              mounts(const char *);
183
184 static void             get_stats(const char *, struct statinfo *, int *, int,
185                                         int);
186 static int              has_stats(const unsigned int *);
187 static int              has_rpcstats(const unsigned int *, int);
188 static void             diff_stats(struct statinfo *, struct statinfo *, int);
189 static void             unpause(int);
190 static void             update_old_counters(struct statinfo *, struct statinfo *);
191
192 static time_t           starttime;
193
194 #define PRNT_CALLS      0x0001
195 #define PRNT_RPC        0x0002
196 #define PRNT_NET        0x0004
197 #define PRNT_FH         0x0008
198 #define PRNT_RC         0x0010
199 #define PRNT_AUTO       0x1000
200 #define PRNT_V2         0x2000
201 #define PRNT_V3         0x4000
202 #define PRNT_V4         0x8000
203 #define PRNT_ALL        0x0fff
204
205 int versions[] = {
206         PRNT_V2,
207         PRNT_V3,
208         PRNT_V4
209 };
210
211 void usage(char *name)
212 {
213         printf("Usage: %s [OPTION]...\n\
214 \n\
215   -m, --mounts          Show statistics on mounted NFS filesystems\n\
216   -c, --client          Show NFS client statistics\n\
217   -s, --server          Show NFS server statistics\n\
218   -2                    Show NFS version 2 statistics\n\
219   -3                    Show NFS version 3 statistics\n\
220   -4                    Show NFS version 4 statistics\n\
221   -o [facility]         Show statistics on particular facilities.\n\
222      nfs                NFS protocol information\n\
223      rpc                General RPC information\n\
224      net                Network layer statistics\n\
225      fh                 Usage information on the server's file handle cache\n\
226      rc                 Usage information on the server's request reply cache\n\
227      all                Select all of the above\n\
228   -v, --verbose, --all  Same as '-o all'\n\
229   -r, --rpc             Show RPC statistics\n\
230   -n, --nfs             Show NFS statistics\n\
231   -Z[#], --sleep[=#]    Collects stats until interrupted.\n\
232                             Cumulative stats are then printed\n\
233                             If # is provided, stats will be output every\n\
234                             # seconds.\n\
235   -S, --since file      Shows difference between current stats and those in 'file'\n\
236   --version             Show program version\n\
237   --help                What you just did\n\
238 \n", name);
239         exit(0);
240 }
241
242 static struct option longopts[] =
243 {
244         { "acl", 0, 0, 'a' },
245         { "all", 0, 0, 'v' },
246         { "auto", 0, 0, '\3' },
247         { "client", 0, 0, 'c' },
248         { "mounted", 0, 0, 'm' },
249         { "nfs", 0, 0, 'n' },
250         { "rpc", 0, 0, 'r' },
251         { "server", 0, 0, 's' },
252         { "verbose", 0, 0, 'v' },
253         { "zero", 0, 0, 'z' },
254         { "help", 0, 0, '\1' },
255         { "version", 0, 0, '\2' },
256         { "sleep", 2, 0, 'Z' },
257         { "since", 1, 0, 'S' },
258         { NULL, 0, 0, 0 }
259 };
260 int opt_sleep;
261
262 int
263 main(int argc, char **argv)
264 {
265         int             opt_all = 0,
266                         opt_srv = 0,
267                         opt_clt = 0,
268                         opt_prt = 0,
269                         sleep_time = 0,
270                         opt_since = 0;
271         int             c;
272         char           *progname,
273                        *serverfile = NFSSRVSTAT,
274                        *clientfile = NFSCLTSTAT;
275
276         struct statinfo *serverinfo = srvinfo,
277                         *serverinfo_tmp = srvinfo_old,
278                         *clientinfo = cltinfo,
279                         *clientinfo_tmp = cltinfo_old;
280
281         struct sigaction act = {
282                 .sa_handler = unpause,
283                 .sa_flags = SA_ONESHOT,
284         };
285
286         if ((progname = strrchr(argv[0], '/')))
287                 progname++;
288         else
289                 progname = argv[0];
290
291         while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrsz\1\2", longopts, NULL)) != EOF) {
292                 switch (c) {
293                 case 'a':
294                         fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
295                         return -1;
296                 case 'c':
297                         opt_clt = 1;
298                         break;
299                 case 'n':
300                         opt_prt |= PRNT_CALLS;
301                         break;
302                 case 'o':
303                         if (!strcmp(optarg, "nfs"))
304                                 opt_prt |= PRNT_CALLS;
305                         else if (!strcmp(optarg, "rpc"))
306                                 opt_prt |= PRNT_RPC;
307                         else if (!strcmp(optarg, "net"))
308                                 opt_prt |= PRNT_NET;
309                         else if (!strcmp(optarg, "rc"))
310                                 opt_prt |= PRNT_RC;
311                         else if (!strcmp(optarg, "fh"))
312                                 opt_prt |= PRNT_FH;
313                         else if (!strcmp(optarg, "all"))
314                                 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
315                         else {
316                                 fprintf(stderr, "nfsstat: unknown category: "
317                                                 "%s\n", optarg);
318                                 return 2;
319                         }
320                         break;
321                 case 'Z':
322                         opt_sleep = 1;
323                         if (optarg) {
324                                 sleep_time = atoi(optarg);
325                         }
326                         break;
327                 case 'S':
328                         opt_since = 1;
329                         serverfile = optarg;
330                         clientfile = optarg;
331                         break;
332                 case '2':
333                 case '3':
334                 case '4':
335                         opt_prt |= versions[c - '2'];
336                         break;
337                 case 'v':
338                         opt_all = 1;
339                         break;
340                 case '\3':
341                         opt_prt |= PRNT_AUTO;
342                         break;
343                 case 'r':
344                         opt_prt |= PRNT_RPC;
345                         break;
346                 case 's':
347                         opt_srv = 1;
348                         break;
349                 case 'z':
350                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
351                                         "not yet supported\n");
352                         return 2;
353                 case 'm':
354                         return mounts(MOUNTSFILE);
355                 case '\1':
356                         usage(progname);
357                         return 0;
358                 case '\2':
359                         fprintf(stdout, "nfsstat: " VERSION "\n");
360                         return 0;
361                 default:
362                         printf("Try `%s --help' for more information.\n", progname);
363                         return -1;
364                 }
365         }
366
367         if (opt_all) {
368                 opt_srv = opt_clt = 1;
369                 opt_prt |= PRNT_ALL;
370         }
371         if (!(opt_srv + opt_clt))
372                 opt_srv = opt_clt = 1;
373         if (!(opt_prt & 0xfff)) {
374                 opt_prt |= PRNT_CALLS + PRNT_RPC;
375         }
376         if (!(opt_prt & 0xe000)) {
377                 opt_prt |= PRNT_AUTO;
378         }
379         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
380                 fprintf(stderr,
381                         "You requested file handle or request cache "
382                         "statistics while using the -c option.\n"
383                         "This information is available only for the NFS "
384                         "server.\n");
385         }
386
387         if (opt_since || opt_sleep) {
388                 serverinfo = srvinfo_old;
389                 serverinfo_tmp = srvinfo;
390                 clientinfo = cltinfo_old;
391                 clientinfo_tmp = cltinfo;
392         }
393
394         if (opt_srv)
395                 get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
396         if (opt_clt)
397                 get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
398
399         if (opt_sleep && !sleep_time) {
400                 starttime = time(NULL);
401                 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
402                 if (sigaction(SIGINT, &act, NULL) != 0) {
403                         fprintf(stderr, "Error: couldn't register for signal and pause.\n");
404                         return 1;
405                 }
406                 pause();
407         }
408
409         if (opt_since || opt_sleep) {
410                 if (opt_srv) {
411                         get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
412                         diff_stats(serverinfo_tmp, serverinfo, 1);
413                 }
414                 if (opt_clt) {
415                         get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
416                         diff_stats(clientinfo_tmp, clientinfo, 0);
417                 }
418         }
419         if(sleep_time) {
420                 while(1) {
421                         if (opt_srv) {
422                                 get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
423                                 diff_stats(serverinfo_tmp, serverinfo, 1);
424                         }
425                         if (opt_clt) {
426                                 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
427                                 diff_stats(clientinfo_tmp, clientinfo, 0);
428                         }
429                         print_all_stats(opt_srv, opt_clt, opt_prt);
430                         fflush(stdout);
431
432                         update_old_counters(clientinfo_tmp, clientinfo);
433                         sleep(sleep_time);
434                 }       
435         } else {
436                 print_all_stats(opt_srv, opt_clt, opt_prt);
437         }
438
439         return 0;
440 }
441
442 static void
443 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
444 {
445         print_server_stats(opt_srv, opt_prt);
446         print_client_stats(opt_clt, opt_prt);
447 }
448
449 static void 
450 print_server_stats(int opt_srv, int opt_prt) 
451 {
452         if (opt_srv) {
453                 if (opt_prt & PRNT_NET) {
454                         print_numbers(
455                         LABEL_srvnet
456                         "packets    udp        tcp        tcpconn\n",
457                         srvnetinfo, 4
458                         );
459                         printf("\n");
460                 }
461                 if (opt_prt & PRNT_RPC) {
462                         if (!opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
463                                 print_numbers(
464                                 LABEL_srvrpc
465                                 "calls      badcalls   badauth    badclnt    xdrcall\n",
466                                 srvrpcinfo, 5
467                                 );
468                                 printf("\n");
469                         }
470                 }
471                 if (opt_prt & PRNT_RC) {
472                         print_numbers(
473                         LABEL_srvrc
474                         "hits       misses     nocache\n",
475                         srvrcinfo, 3
476                         );
477                         printf("\n");
478                 }
479
480                 /*
481                  * 2.2 puts all fh-related info after the 'rc' header
482                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
483                  *     'stale' to the start and swaps dir and nondir :-(  
484                  *     We preseve the 2.2 order
485                  */
486                 if (opt_prt & PRNT_FH) {
487                         if (get_stat_info("fh", srvinfo)) {     /* >= 2.4 */
488                                 int t = srvfhinfo[3];
489                                 srvfhinfo[3]=srvfhinfo[4];
490                                 srvfhinfo[4]=t;
491                                 
492                                 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
493                                 
494                                 print_numbers(
495                                         LABEL_srvfh
496                                         "lookup     anon       ncachedir  ncachedir  stale\n",
497                                         srvfhinfo + 1, 5);
498                         } else                                  /* < 2.4 */
499                                 print_numbers(
500                                         LABEL_srvfh
501                                         "lookup     anon       ncachedir  ncachedir  stale\n",
502                                         srvrcinfo + 3, 5);
503                         printf("\n");
504                 }
505                 if (opt_prt & PRNT_CALLS) {
506                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
507                                 print_callstats(
508                                 LABEL_srvproc2,
509                                 nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
510                                 );
511                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
512                                 print_callstats(
513                                 LABEL_srvproc3,
514                                 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
515                                 );
516                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
517                                 print_callstats(
518                                 LABEL_srvproc4,
519                                 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
520                                 );
521                                 print_callstats(
522                                 LABEL_srvproc4ops,
523                                 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
524                                 );
525                         }
526                 }
527         }
528 }
529 static void
530 print_client_stats(int opt_clt, int opt_prt) 
531 {
532         if (opt_clt) {
533                 if (opt_prt & PRNT_NET) {
534                         print_numbers(
535                         LABEL_cltnet
536                         "packets    udp        tcp        tcpconn\n",
537                         cltnetinfo, 4
538                         );
539                         printf("\n");
540                 }
541                 if (opt_prt & PRNT_RPC) {
542                         if (!opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
543                                 print_numbers(
544                                 LABEL_cltrpc
545                                 "calls      retrans    authrefrsh\n",
546                                 cltrpcinfo, 3
547                                 );
548                                 printf("\n");
549                         }
550                 }
551                 if (opt_prt & PRNT_CALLS) {
552                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
553                                 print_callstats(
554                                 LABEL_cltproc2,
555                                 nfsv2name, cltproc2info + 1,  sizeof(nfsv2name)/sizeof(char *)
556                                 );
557                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
558                                 print_callstats(
559                                 LABEL_cltproc3,
560                                 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
561                                 );
562                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
563                                 print_callstats(
564                                 LABEL_cltproc4,
565                                 nfscltproc4name, cltproc4info + 1,  sizeof(nfscltproc4name)/sizeof(char *)
566                                 );
567                 }
568         }
569 }
570
571 static statinfo *
572 get_stat_info(const char *sp, struct statinfo *statp)
573 {
574         struct statinfo *ip;
575
576         for (ip = statp; ip->tag; ip++) {
577                 if (!strcmp(sp, ip->tag))
578                         return ip;
579         }
580
581         return NULL;
582 }
583
584 static void
585 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
586 {
587         unsigned int    i;
588
589         fputs(hdr, stdout);
590         for (i = 0; i < nr; i++)
591                 printf("%s%-8u", i? "   " : "", info[i]);
592         printf("\n");
593 }
594
595 static void
596 print_callstats(const char *hdr, const char **names,
597                                  unsigned int *info, unsigned int nr)
598 {
599         unsigned long long      total;
600         unsigned long long      pct;
601         int             i, j;
602
603         fputs(hdr, stdout);
604         for (i = 0, total = 0; i < nr; i++)
605                 total += info[i];
606         if (!total)
607                 total = 1;
608         for (i = 0; i < nr; i += 6) {
609                 for (j = 0; j < 6 && i + j < nr; j++)
610                         printf("%-13s", names[i+j]);
611                 printf("\n");
612                 for (j = 0; j < 6 && i + j < nr; j++) {
613                         pct = ((unsigned long long) info[i+j]*100)/total;
614                         printf("%-8u%3llu%% ", info[i+j], pct);
615                 }
616                 printf("\n");
617         }
618         printf("\n");
619 }
620
621 /* returns 0 on success, 1 otherwise */
622 static int
623 parse_raw_statfile(const char *name, struct statinfo *statp)
624 {
625         char    buffer[4096], *next;
626         FILE    *fp;
627
628         /* Being unable to read e.g. the nfsd stats file shouldn't
629          * be a fatal error -- it usually means the module isn't loaded.
630          */
631         if ((fp = fopen(name, "r")) == NULL) {
632                 // fprintf(stderr, "Warning: %s: %m\n", name);
633                 return 1;
634         }
635
636         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
637                 struct statinfo *ip;
638                 char            *sp, *line = buffer;
639                 unsigned int    i, cnt;
640                 unsigned int    total = 0;
641
642                 if ((next = strchr(line, '\n')) != NULL)
643                         *next++ = '\0';
644                 if (!(sp = strtok(line, " \t")))
645                         continue;
646
647                 ip = get_stat_info(sp, statp);
648                 if (!ip)
649                         continue;
650
651                 cnt = ip->nrvals;
652
653                 for (i = 0; i < cnt; i++) {
654                         if (!(sp = strtok(NULL, " \t")))
655                                 break;
656                         ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
657                         total += ip->valptr[i];
658                 }
659                 ip->valptr[cnt - 1] = total;
660         }
661
662         fclose(fp);
663         return 0;
664 }
665
666 /* returns 0 on success, 1 otherwise */
667 static int
668 parse_pretty_statfile(const char *filename, struct statinfo *info)
669 {
670         int numvals, curindex, numconsumed, n, err = 1;
671         unsigned int sum;
672         char buf[4096], *bufp, *fmt, is_proc;
673         FILE *fp = NULL;
674         struct statinfo *ip;
675
676         if ((fp = fopen(filename, "r")) == NULL)
677                 //err(2, "Unable to open statfile '%s'.\n", filename);
678                 goto out;
679
680         while (fgets(buf, sizeof(buf), fp) != NULL) {
681                 for (ip = info; ip->tag; ip++) {
682                         if (strcmp(buf, ip->label))
683                                 continue;
684
685                         sum = 0;
686                         numvals = ip->nrvals - 1;
687                         is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
688                         if (is_proc) {
689                                 fmt = " %u %*u%% %n";
690                                 curindex = 1;
691                                 ip->valptr[0] = 0;
692                         } else {
693                                 fmt = " %u %n";
694                                 curindex = 0;
695                         }
696 more_stats:
697                         /* get (and skip) header */
698                         if (fgets(buf, sizeof(buf), fp) == NULL) {
699                                 fprintf(stderr, "Failed to locate header after "
700                                                 "label for '%s' in %s.\n",
701                                                 ip->tag, filename);
702                                 goto out;
703                         }
704                         /* no header -- done with this "tag" */
705                         if (*buf == '\n') {
706                                 ip->valptr[numvals] = sum;
707                                 break;
708                         }
709                         /* get stats */
710                         if (fgets(buf, sizeof(buf), fp) == NULL) {
711                                 fprintf(stderr, "Failed to locate stats after "
712                                                 "header for '%s' in %s.\n",
713                                                 ip->tag, filename);
714                                 goto out;
715                         }
716                         bufp = buf;
717                         for (; curindex < numvals; curindex++) {
718                                 n = sscanf(bufp, fmt, &ip->valptr[curindex],
719                                                 &numconsumed);
720                                 if (n != 1)
721                                         break;
722                                 if (is_proc) {
723                                         ip->valptr[0]++;
724                                         sum++;
725                                 }
726                                 sum += ip->valptr[curindex];
727                                 bufp += numconsumed;
728                         }
729                         goto more_stats;
730                 }
731         }
732         err = 0;
733 out:
734         if (fp)
735                 fclose(fp);
736         return err;
737 }
738
739 static int
740 mounts(const char *name)
741 {
742         char    buffer[4096], *next;
743         FILE    *fp;
744
745         /* Being unable to read e.g. the nfsd stats file shouldn't
746          * be a fatal error -- it usually means the module isn't loaded.
747          */
748         if ((fp = fopen(name, "r")) == NULL) {
749                 fprintf(stderr, "Warning: %s: %m\n", name);
750                 return 0;
751         }
752
753         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
754                 char          *line = buffer;
755                 char          *device, *mount, *type, *flags;
756
757                 if ((next = strchr(line, '\n')) != NULL)
758                         *next = '\0';
759
760                 if (!(device = strtok(line, " \t")))
761                         continue;
762
763                 if (!(mount = strtok(NULL, " \t")))
764                         continue;
765
766                 if (!(type = strtok(NULL, " \t")))
767                         continue;
768
769                 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
770                     continue;
771                 }
772
773                 if (!(flags = strtok(NULL, " \t")))
774                         continue;
775
776                 printf("%s from %s\n", mount, device);
777                 printf(" Flags:\t%s\n", flags);
778                 printf("\n");
779
780                 continue;
781         }
782
783         fclose(fp);
784         return 1;
785 }
786
787 static void
788 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
789                 int is_srv)
790 {
791         FILE *fp;
792         char buf[10];
793         int err = 1;
794         char *label = is_srv ? "Server" : "Client";
795
796         /* try to guess what type of stat file we're dealing with */
797         if ((fp = fopen(file, "r")) == NULL)
798                 goto out;
799         if (fgets(buf, 10, fp) == NULL)
800                 goto out;
801         if (!strncmp(buf, "net ", 4)) {
802                 /* looks like raw client stats */
803                 if (is_srv) {
804                         fprintf(stderr, "Warning: no server info present in "
805                                         "raw client stats file.\n");
806                         *opt = 0;
807                 } else
808                         err = parse_raw_statfile(file, info);
809         } else if (!strncmp(buf, "rc ", 3)) {
810                 /* looks like raw server stats */
811                 if (!is_srv) {
812                         fprintf(stderr, "Warning: no client info present in "
813                                         "raw server stats file.\n");
814                         *opt = 0;
815                 } else
816                         err = parse_raw_statfile(file, info);
817         } else
818                 /* looks like pretty client and server stats */
819                 err = parse_pretty_statfile(file, info);
820 out:
821         if (fp)
822                 fclose(fp);
823         if (err) {
824                 if (!other_opt) {
825                         fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
826                                         label, file);
827                         exit(2);
828                 }
829                 *opt = 0;
830         }
831 }
832
833 /*
834  * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
835  * denotes the number of subsequent entries.  statinfo value arrays contain an additional
836  * field at the end which contains the sum of all previous elements in the array -- so,
837  * there are stats if the sum's greater than the entry-count.
838  */
839 static int
840 has_stats(const unsigned int *info)
841 {
842         return (info[0] && info[info[0] + 1] > info[0]);
843 }
844 static int
845 has_rpcstats(const unsigned int *info, int size)
846 {
847         int i, cnt;
848
849         for (i=0, cnt=0; i < size; i++)
850                 cnt += info[i];
851         return cnt;
852 }
853
854 /*
855  * take the difference of each individual stat value in 'new' and 'old'
856  * and store the results back into 'new'
857  */
858 static void
859 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
860 {
861         int i, j, nodiff_first_index, should_diff;
862
863         /*
864          * Different stat types have different formats in the /proc
865          * files: for the proc2/3/4-type stats, the first entry has
866          * the total number of subsequent entries; one does not want
867          * to diff that first entry.  The other stat types aren't like
868          * this.  So, we diff a given entry if it's not of one of the
869          * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
870          * the first entry ("j" > 0).
871          */
872         nodiff_first_index = 2 + (2 * is_srv);
873
874         for (i = 0; old[i].tag; i++) {
875                 for (j = 0; j < new[i].nrvals; j++) {
876                         should_diff = (i < nodiff_first_index || j > 0);
877                         if (should_diff)
878                                 new[i].valptr[j] -= old[i].valptr[j];
879                 }
880
881                 /*
882                  * Make sure that the "totals" entry (last value in
883                  * each stat array) for the procX-type stats has the
884                  * "numentries" entry's (first value in procX-type
885                  * stat arrays) constant value added-back after the
886                  * diff -- i.e., it should always be included in the
887                  * total.
888                  */
889                 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
890                         new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
891         }
892 }
893
894 static void
895 unpause(int sig)
896 {
897         double time_diff;
898         int minutes, seconds;
899         time_t endtime;
900
901         endtime = time(NULL);
902         time_diff = difftime(endtime, starttime);
903         minutes = time_diff / 60;
904         seconds = (int)time_diff % 60;
905         printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
906 }
907
908 static void
909 update_old_counters(struct statinfo *new, struct statinfo *old)
910 {
911         int z, i;
912         for (z = 0; old[z].tag; z++) 
913                 for (i = 0; i <= old[z].nrvals; i++) 
914                         old[z].valptr[i] += new[z].valptr[i];
915
916 }