The --list option does not work on server stats.
[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_stats_list(int, int, int);
174 static void             print_numbers(const char *, unsigned int *,
175                                         unsigned int);
176 static void             print_callstats(const char *, const char **,
177                                         unsigned int *, unsigned int);
178 static void             print_callstats_list(const char *, const char **,
179                                         unsigned int *, unsigned int);
180 static int              parse_raw_statfile(const char *, struct statinfo *);
181 static int              parse_pretty_statfile(const char *, struct statinfo *);
182
183 static statinfo         *get_stat_info(const char *, struct statinfo *);
184
185 static int              mounts(const char *);
186
187 static void             get_stats(const char *, struct statinfo *, int *, int,
188                                         int);
189 static int              has_stats(const unsigned int *);
190 static int              has_rpcstats(const unsigned int *, int);
191 static void             diff_stats(struct statinfo *, struct statinfo *, int);
192 static void             unpause(int);
193 static void             update_old_counters(struct statinfo *, struct statinfo *);
194
195 static time_t           starttime;
196
197 #define PRNT_CALLS      0x0001
198 #define PRNT_RPC        0x0002
199 #define PRNT_NET        0x0004
200 #define PRNT_FH         0x0008
201 #define PRNT_RC         0x0010
202 #define PRNT_AUTO       0x1000
203 #define PRNT_V2         0x2000
204 #define PRNT_V3         0x4000
205 #define PRNT_V4         0x8000
206 #define PRNT_ALL        0x0fff
207
208 int versions[] = {
209         PRNT_V2,
210         PRNT_V3,
211         PRNT_V4
212 };
213
214 void usage(char *name)
215 {
216         printf("Usage: %s [OPTION]...\n\
217 \n\
218   -m, --mounts          Show statistics on mounted NFS filesystems\n\
219   -c, --client          Show NFS client statistics\n\
220   -s, --server          Show NFS server statistics\n\
221   -2                    Show NFS version 2 statistics\n\
222   -3                    Show NFS version 3 statistics\n\
223   -4                    Show NFS version 4 statistics\n\
224   -o [facility]         Show statistics on particular facilities.\n\
225      nfs                NFS protocol information\n\
226      rpc                General RPC information\n\
227      net                Network layer statistics\n\
228      fh                 Usage information on the server's file handle cache\n\
229      rc                 Usage information on the server's request reply cache\n\
230      all                Select all of the above\n\
231   -v, --verbose, --all  Same as '-o all'\n\
232   -r, --rpc             Show RPC statistics\n\
233   -n, --nfs             Show NFS statistics\n\
234   -Z[#], --sleep[=#]    Collects stats until interrupted.\n\
235                             Cumulative stats are then printed\n\
236                             If # is provided, stats will be output every\n\
237                             # seconds.\n\
238   -S, --since file      Shows difference between current stats and those in 'file'\n\
239   -l, --list            Prints stats in list format\n\
240   --version             Show program version\n\
241   --help                What you just did\n\
242 \n", name);
243         exit(0);
244 }
245
246 static struct option longopts[] =
247 {
248         { "acl", 0, 0, 'a' },
249         { "all", 0, 0, 'v' },
250         { "auto", 0, 0, '\3' },
251         { "client", 0, 0, 'c' },
252         { "mounted", 0, 0, 'm' },
253         { "nfs", 0, 0, 'n' },
254         { "rpc", 0, 0, 'r' },
255         { "server", 0, 0, 's' },
256         { "verbose", 0, 0, 'v' },
257         { "zero", 0, 0, 'z' },
258         { "help", 0, 0, '\1' },
259         { "version", 0, 0, '\2' },
260         { "sleep", 2, 0, 'Z' },
261         { "since", 1, 0, 'S' },
262         { "list", 0, 0, 'l' },
263         { NULL, 0, 0, 0 }
264 };
265 int opt_sleep;
266
267 int
268 main(int argc, char **argv)
269 {
270         int             opt_all = 0,
271                         opt_srv = 0,
272                         opt_clt = 0,
273                         opt_prt = 0,
274                         sleep_time = 0,
275                         opt_list =0,
276                         opt_since = 0;
277         int             c;
278         char           *progname,
279                        *serverfile = NFSSRVSTAT,
280                        *clientfile = NFSCLTSTAT;
281
282         struct statinfo *serverinfo = srvinfo,
283                         *serverinfo_tmp = srvinfo_old,
284                         *clientinfo = cltinfo,
285                         *clientinfo_tmp = cltinfo_old;
286
287         struct sigaction act = {
288                 .sa_handler = unpause,
289                 .sa_flags = SA_ONESHOT,
290         };
291
292         if ((progname = strrchr(argv[0], '/')))
293                 progname++;
294         else
295                 progname = argv[0];
296
297         while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrslz\1\2", longopts, NULL)) != EOF) {
298                 switch (c) {
299                 case 'a':
300                         fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
301                         return -1;
302                 case 'c':
303                         opt_clt = 1;
304                         break;
305                 case 'n':
306                         opt_prt |= PRNT_CALLS;
307                         break;
308                 case 'o':
309                         if (!strcmp(optarg, "nfs"))
310                                 opt_prt |= PRNT_CALLS;
311                         else if (!strcmp(optarg, "rpc"))
312                                 opt_prt |= PRNT_RPC;
313                         else if (!strcmp(optarg, "net"))
314                                 opt_prt |= PRNT_NET;
315                         else if (!strcmp(optarg, "rc"))
316                                 opt_prt |= PRNT_RC;
317                         else if (!strcmp(optarg, "fh"))
318                                 opt_prt |= PRNT_FH;
319                         else if (!strcmp(optarg, "all"))
320                                 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
321                         else {
322                                 fprintf(stderr, "nfsstat: unknown category: "
323                                                 "%s\n", optarg);
324                                 return 2;
325                         }
326                         break;
327                 case 'Z':
328                         opt_sleep = 1;
329                         if (optarg) {
330                                 sleep_time = atoi(optarg);
331                         }
332                         break;
333                 case 'S':
334                         opt_since = 1;
335                         serverfile = optarg;
336                         clientfile = optarg;
337                         break;
338                 case '2':
339                 case '3':
340                 case '4':
341                         opt_prt |= versions[c - '2'];
342                         break;
343                 case 'v':
344                         opt_all = 1;
345                         break;
346                 case '\3':
347                         opt_prt |= PRNT_AUTO;
348                         break;
349                 case 'r':
350                         opt_prt |= PRNT_RPC;
351                         break;
352                 case 's':
353                         opt_srv = 1;
354                         break;
355                 case 'l':
356                         opt_list = 1;
357                         break;
358                 case 'z':
359                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
360                                         "not yet supported\n");
361                         return 2;
362                 case 'm':
363                         return mounts(MOUNTSFILE);
364                 case '\1':
365                         usage(progname);
366                         return 0;
367                 case '\2':
368                         fprintf(stdout, "nfsstat: " VERSION "\n");
369                         return 0;
370                 default:
371                         printf("Try `%s --help' for more information.\n", progname);
372                         return -1;
373                 }
374         }
375
376         if (opt_all) {
377                 opt_srv = opt_clt = 1;
378                 opt_prt |= PRNT_ALL;
379         }
380         if (!(opt_srv + opt_clt))
381                 opt_srv = opt_clt = 1;
382         if (!(opt_prt & 0xfff)) {
383                 opt_prt |= PRNT_CALLS + PRNT_RPC;
384         }
385         if (!(opt_prt & 0xe000)) {
386                 opt_prt |= PRNT_AUTO;
387         }
388         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
389                 fprintf(stderr,
390                         "You requested file handle or request cache "
391                         "statistics while using the -c option.\n"
392                         "This information is available only for the NFS "
393                         "server.\n");
394         }
395
396         if (opt_since || opt_sleep) {
397                 serverinfo = srvinfo_old;
398                 serverinfo_tmp = srvinfo;
399                 clientinfo = cltinfo_old;
400                 clientinfo_tmp = cltinfo;
401         }
402
403         if (opt_srv)
404                 get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
405         if (opt_clt)
406                 get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
407
408         if (opt_sleep && !sleep_time) {
409                 starttime = time(NULL);
410                 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
411                 if (sigaction(SIGINT, &act, NULL) != 0) {
412                         fprintf(stderr, "Error: couldn't register for signal and pause.\n");
413                         return 1;
414                 }
415                 pause();
416         }
417
418         if (opt_since || opt_sleep) {
419                 if (opt_srv) {
420                         get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
421                         diff_stats(serverinfo_tmp, serverinfo, 1);
422                 }
423                 if (opt_clt) {
424                         get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
425                         diff_stats(clientinfo_tmp, clientinfo, 0);
426                 }
427         }
428         if(sleep_time) {
429                 while(1) {
430                         if (opt_srv) {
431                                 get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
432                                 diff_stats(serverinfo_tmp, serverinfo, 1);
433                         }
434                         if (opt_clt) {
435                                 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
436                                 diff_stats(clientinfo_tmp, clientinfo, 0);
437                         }
438                         if (opt_list) {
439                                 print_stats_list(opt_srv, opt_clt, opt_prt);
440                         } else {
441                                 print_all_stats(opt_srv, opt_clt, opt_prt);
442                         }
443                         fflush(stdout);
444
445                         if (opt_srv)
446                                 update_old_counters(serverinfo_tmp, serverinfo);
447                         if (opt_clt)
448                                 update_old_counters(clientinfo_tmp, clientinfo);
449
450                         sleep(sleep_time);
451                 }       
452         } else {
453                 if (opt_list) {
454                         print_stats_list(opt_srv, opt_clt, opt_prt);
455                 } else {
456                         print_all_stats(opt_srv, opt_clt, opt_prt);
457                 }
458         }
459
460         return 0;
461 }
462
463 static void
464 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
465 {
466         print_server_stats(opt_srv, opt_prt);
467         print_client_stats(opt_clt, opt_prt);
468 }
469
470 static void 
471 print_server_stats(int opt_srv, int opt_prt) 
472 {
473         if (!opt_srv)
474                 return;
475
476         if (opt_prt & PRNT_NET) {
477                 if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
478                 } else {
479                         print_numbers( LABEL_srvnet
480                                 "packets    udp        tcp        tcpconn\n",
481                         srvnetinfo, 4);
482                         printf("\n");
483                 }
484         }
485         if (opt_prt & PRNT_RPC) {
486                 if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
487                         ;
488                 } else {
489                         print_numbers(LABEL_srvrpc
490                                 "calls      badcalls   badauth    badclnt    xdrcall\n",
491                                 srvrpcinfo, 5);
492                         printf("\n");
493                 }
494         }
495         if (opt_prt & PRNT_RC) {
496                 if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
497                         ;
498                 } else {
499                         print_numbers(LABEL_srvrc
500                                 "hits       misses     nocache\n",
501                                 srvrcinfo, 3);
502                         printf("\n");
503                 }
504         }
505
506         /*
507          * 2.2 puts all fh-related info after the 'rc' header
508          * 2.4 puts all fh-related info after the 'fh' header, but relocates
509          *     'stale' to the start and swaps dir and nondir :-(  
510          *     We preseve the 2.2 order
511          */
512         if (opt_prt & PRNT_FH) {
513                 if (get_stat_info("fh", srvinfo)) {     /* >= 2.4 */
514                         int t = srvfhinfo[3];
515                         srvfhinfo[3]=srvfhinfo[4];
516                         srvfhinfo[4]=t;
517                         
518                         srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
519                         
520                         print_numbers(
521                                 LABEL_srvfh
522                                 "lookup     anon       ncachedir  ncachedir  stale\n",
523                                 srvfhinfo + 1, 5);
524                 } else                                  /* < 2.4 */
525                         print_numbers(
526                                 LABEL_srvfh
527                                 "lookup     anon       ncachedir  ncachedir  stale\n",
528                                 srvrcinfo + 3, 5);
529                 printf("\n");
530         }
531         if (opt_prt & PRNT_CALLS) {
532                 if ((opt_prt & PRNT_V2) || 
533                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
534                         if (opt_sleep && !has_stats(srvproc2info)) {
535                                 ;
536                         } else {
537                                 print_callstats(LABEL_srvproc2,
538                                         nfsv2name, srvproc2info + 1, 
539                                         sizeof(nfsv2name)/sizeof(char *));
540                         }
541                 }
542                 if ((opt_prt & PRNT_V3) || 
543                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
544                         if (opt_sleep && !has_stats(srvproc3info)) {
545                                 ;
546                         } else {
547                                 print_callstats(LABEL_srvproc3,
548                                         nfsv3name, srvproc3info + 1, 
549                                         sizeof(nfsv3name)/sizeof(char *));
550                         }
551                 }
552                 if ((opt_prt & PRNT_V4) || 
553                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
554                         if (opt_sleep && !has_stats(srvproc4info)) {
555                                 ;
556                         } else {
557                                 print_callstats( LABEL_srvproc4,
558                                         nfssrvproc4name, srvproc4info + 1, 
559                                         sizeof(nfssrvproc4name)/sizeof(char *));
560                                 print_callstats(LABEL_srvproc4ops,
561                                         nfssrvproc4opname, srvproc4opsinfo + 1, 
562                                         sizeof(nfssrvproc4opname)/sizeof(char *));
563                         }
564                 }
565         }
566 }
567 static void
568 print_client_stats(int opt_clt, int opt_prt) 
569 {
570         if (!opt_clt)
571                 return;
572
573         if (opt_prt & PRNT_NET) {
574                 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
575                         ;
576                 } else { 
577                         print_numbers(LABEL_cltnet
578                                 "packets    udp        tcp        tcpconn\n",
579                                 cltnetinfo, 4);
580                         printf("\n");
581                 }
582         }
583         if (opt_prt & PRNT_RPC) {
584                 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
585                         ;
586                 } else {
587                         print_numbers(LABEL_cltrpc
588                                 "calls      retrans    authrefrsh\n",
589                                 cltrpcinfo, 3);
590                         printf("\n");
591                 }
592         }
593         if (opt_prt & PRNT_CALLS) {
594                 if ((opt_prt & PRNT_V2) || 
595                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
596                         if (opt_sleep && !has_stats(cltproc2info)) {
597                                 ;
598                         } else {
599                                 print_callstats(LABEL_cltproc2,
600                                         nfsv2name, cltproc2info + 1,  
601                                         sizeof(nfsv2name)/sizeof(char *));
602                         }
603                 }
604                 if ((opt_prt & PRNT_V3) || 
605                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
606                         if (opt_sleep && !has_stats(cltproc3info)) {
607                                 ;
608                         } else {
609                                 print_callstats(LABEL_cltproc3,
610                                         nfsv3name, cltproc3info + 1, 
611                                         sizeof(nfsv3name)/sizeof(char *));
612                         }
613                 }
614                 if ((opt_prt & PRNT_V4) || 
615                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
616                         if (opt_sleep && !has_stats(cltproc4info)) {
617                                 ;
618                         } else {
619                                 print_callstats(LABEL_cltproc4,
620                                         nfscltproc4name, cltproc4info + 1,  
621                                         sizeof(nfscltproc4name)/sizeof(char *));
622                         }
623                 }
624         }
625 }
626
627 static void
628 print_clnt_list(int opt_prt) 
629 {
630         if (opt_prt & PRNT_CALLS) {
631                 if ((opt_prt & PRNT_V2) || 
632                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
633                         if (opt_sleep && !has_stats(cltproc2info)) {
634                                 ;
635                         } else {
636                                 print_callstats_list("nfs v2 client",
637                                         nfsv2name, cltproc2info + 1,  
638                                         sizeof(nfsv2name)/sizeof(char *));
639                         }
640                 }
641                 if ((opt_prt & PRNT_V3) || 
642                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
643                         if (opt_sleep && !has_stats(cltproc3info)) {
644                                 ;
645                         } else { 
646                                 print_callstats_list("nfs v3 client",
647                                         nfsv3name, cltproc3info + 1, 
648                                         sizeof(nfsv3name)/sizeof(char *));
649                         }
650                 }
651                 if ((opt_prt & PRNT_V4) || 
652                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
653                         if (opt_sleep && !has_stats(cltproc4info)) {
654                                 ;
655                         } else {
656                                 print_callstats_list("nfs v4 ops",
657                                         nfssrvproc4opname, srvproc4opsinfo + 1, 
658                                         sizeof(nfssrvproc4opname)/sizeof(char *));
659                                 print_callstats_list("nfs v4 client",
660                                         nfscltproc4name, cltproc4info + 1,  
661                                         sizeof(nfscltproc4name)/sizeof(char *));
662                         }
663                 }
664         }
665 }
666 static void
667 print_serv_list(int opt_prt) 
668 {
669         if (opt_prt & PRNT_CALLS) {
670                 if ((opt_prt & PRNT_V2) || 
671                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
672                         if (opt_sleep && !has_stats(srvproc2info)) {
673                                 ;
674                         } else {
675                                 print_callstats_list("nfs v2 server",
676                                         nfsv2name, srvproc2info + 1, 
677                                         sizeof(nfsv2name)/sizeof(char *));
678                         }
679                 }
680                 if ((opt_prt & PRNT_V3) || 
681                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
682                         if (opt_sleep && !has_stats(srvproc3info)) {
683                                 ;
684                         } else {
685                                 print_callstats_list("nfs v3 server",
686                                         nfsv3name, srvproc3info + 1, 
687                                         sizeof(nfsv3name)/sizeof(char *));
688                         }
689                 }
690                 if ((opt_prt & PRNT_V4) || 
691                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) {
692                         if (opt_sleep && !has_stats(srvproc4info)) {
693                                 ;
694                         } else {
695                                 print_callstats_list("nfs v4 ops",
696                                         nfssrvproc4opname, srvproc4opsinfo + 1, 
697                                         sizeof(nfssrvproc4opname)/sizeof(char *));
698                         }
699                 }
700         }
701 }
702 static void
703 print_stats_list(int opt_srv, int opt_clt, int opt_prt) 
704 {
705         if (opt_srv)
706                 print_serv_list(opt_prt);
707
708         if (opt_clt)
709                 print_clnt_list(opt_prt);
710 }
711
712 static statinfo *
713 get_stat_info(const char *sp, struct statinfo *statp)
714 {
715         struct statinfo *ip;
716
717         for (ip = statp; ip->tag; ip++) {
718                 if (!strcmp(sp, ip->tag))
719                         return ip;
720         }
721
722         return NULL;
723 }
724
725 static void
726 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
727 {
728         unsigned int    i;
729
730         fputs(hdr, stdout);
731         for (i = 0; i < nr; i++)
732                 printf("%s%-8u", i? "   " : "", info[i]);
733         printf("\n");
734 }
735
736 static void
737 print_callstats(const char *hdr, const char **names,
738                                  unsigned int *info, unsigned int nr)
739 {
740         unsigned long long      total;
741         unsigned long long      pct;
742         int             i, j;
743
744         fputs(hdr, stdout);
745         for (i = 0, total = 0; i < nr; i++)
746                 total += info[i];
747         if (!total)
748                 total = 1;
749         for (i = 0; i < nr; i += 6) {
750                 for (j = 0; j < 6 && i + j < nr; j++)
751                         printf("%-13s", names[i+j]);
752                 printf("\n");
753                 for (j = 0; j < 6 && i + j < nr; j++) {
754                         pct = ((unsigned long long) info[i+j]*100)/total;
755                         printf("%-8u%3llu%% ", info[i+j], pct);
756                 }
757                 printf("\n");
758         }
759         printf("\n");
760 }
761
762 static void
763 print_callstats_list(const char *hdr, const char **names,
764                         unsigned int *callinfo, unsigned int nr)
765 {
766         unsigned long long      calltotal;
767         int                     i;
768
769         for (i = 0, calltotal = 0; i < nr; i++) {
770                 calltotal += callinfo[i];
771         }
772         if (!calltotal)
773                 return;
774         printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
775         printf("------------- ------------- --------\n");
776         for (i = 0; i < nr; i++) {
777                         if (callinfo[i])
778                                 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
779         }
780         printf("\n");
781                 
782 }
783
784
785 /* returns 0 on success, 1 otherwise */
786 static int
787 parse_raw_statfile(const char *name, struct statinfo *statp)
788 {
789         char    buffer[4096], *next;
790         FILE    *fp;
791
792         /* Being unable to read e.g. the nfsd stats file shouldn't
793          * be a fatal error -- it usually means the module isn't loaded.
794          */
795         if ((fp = fopen(name, "r")) == NULL) {
796                 // fprintf(stderr, "Warning: %s: %m\n", name);
797                 return 1;
798         }
799
800         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
801                 struct statinfo *ip;
802                 char            *sp, *line = buffer;
803                 unsigned int    i, cnt;
804                 unsigned int    total = 0;
805
806                 if ((next = strchr(line, '\n')) != NULL)
807                         *next++ = '\0';
808                 if (!(sp = strtok(line, " \t")))
809                         continue;
810
811                 ip = get_stat_info(sp, statp);
812                 if (!ip)
813                         continue;
814
815                 cnt = ip->nrvals;
816
817                 for (i = 0; i < cnt; i++) {
818                         if (!(sp = strtok(NULL, " \t")))
819                                 break;
820                         ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
821                         total += ip->valptr[i];
822                 }
823                 ip->valptr[cnt - 1] = total;
824         }
825
826         fclose(fp);
827         return 0;
828 }
829
830 /* returns 0 on success, 1 otherwise */
831 static int
832 parse_pretty_statfile(const char *filename, struct statinfo *info)
833 {
834         int numvals, curindex, numconsumed, n, err = 1;
835         unsigned int sum;
836         char buf[4096], *bufp, *fmt, is_proc;
837         FILE *fp = NULL;
838         struct statinfo *ip;
839
840         if ((fp = fopen(filename, "r")) == NULL)
841                 //err(2, "Unable to open statfile '%s'.\n", filename);
842                 goto out;
843
844         while (fgets(buf, sizeof(buf), fp) != NULL) {
845                 for (ip = info; ip->tag; ip++) {
846                         if (strcmp(buf, ip->label))
847                                 continue;
848
849                         sum = 0;
850                         numvals = ip->nrvals - 1;
851                         is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
852                         if (is_proc) {
853                                 fmt = " %u %*u%% %n";
854                                 curindex = 1;
855                                 ip->valptr[0] = 0;
856                         } else {
857                                 fmt = " %u %n";
858                                 curindex = 0;
859                         }
860 more_stats:
861                         /* get (and skip) header */
862                         if (fgets(buf, sizeof(buf), fp) == NULL) {
863                                 fprintf(stderr, "Failed to locate header after "
864                                                 "label for '%s' in %s.\n",
865                                                 ip->tag, filename);
866                                 goto out;
867                         }
868                         /* no header -- done with this "tag" */
869                         if (*buf == '\n') {
870                                 ip->valptr[numvals] = sum;
871                                 break;
872                         }
873                         /* get stats */
874                         if (fgets(buf, sizeof(buf), fp) == NULL) {
875                                 fprintf(stderr, "Failed to locate stats after "
876                                                 "header for '%s' in %s.\n",
877                                                 ip->tag, filename);
878                                 goto out;
879                         }
880                         bufp = buf;
881                         for (; curindex < numvals; curindex++) {
882                                 n = sscanf(bufp, fmt, &ip->valptr[curindex],
883                                                 &numconsumed);
884                                 if (n != 1)
885                                         break;
886                                 if (is_proc) {
887                                         ip->valptr[0]++;
888                                         sum++;
889                                 }
890                                 sum += ip->valptr[curindex];
891                                 bufp += numconsumed;
892                         }
893                         goto more_stats;
894                 }
895         }
896         err = 0;
897 out:
898         if (fp)
899                 fclose(fp);
900         return err;
901 }
902
903 static int
904 mounts(const char *name)
905 {
906         char    buffer[4096], *next;
907         FILE    *fp;
908
909         /* Being unable to read e.g. the nfsd stats file shouldn't
910          * be a fatal error -- it usually means the module isn't loaded.
911          */
912         if ((fp = fopen(name, "r")) == NULL) {
913                 fprintf(stderr, "Warning: %s: %m\n", name);
914                 return 0;
915         }
916
917         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
918                 char          *line = buffer;
919                 char          *device, *mount, *type, *flags;
920
921                 if ((next = strchr(line, '\n')) != NULL)
922                         *next = '\0';
923
924                 if (!(device = strtok(line, " \t")))
925                         continue;
926
927                 if (!(mount = strtok(NULL, " \t")))
928                         continue;
929
930                 if (!(type = strtok(NULL, " \t")))
931                         continue;
932
933                 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
934                     continue;
935                 }
936
937                 if (!(flags = strtok(NULL, " \t")))
938                         continue;
939
940                 printf("%s from %s\n", mount, device);
941                 printf(" Flags:\t%s\n", flags);
942                 printf("\n");
943
944                 continue;
945         }
946
947         fclose(fp);
948         return 1;
949 }
950
951 static void
952 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
953                 int is_srv)
954 {
955         FILE *fp;
956         char buf[10];
957         int err = 1;
958         char *label = is_srv ? "Server" : "Client";
959
960         /* try to guess what type of stat file we're dealing with */
961         if ((fp = fopen(file, "r")) == NULL)
962                 goto out;
963         if (fgets(buf, 10, fp) == NULL)
964                 goto out;
965         if (!strncmp(buf, "net ", 4)) {
966                 /* looks like raw client stats */
967                 if (is_srv) {
968                         fprintf(stderr, "Warning: no server info present in "
969                                         "raw client stats file.\n");
970                         *opt = 0;
971                 } else
972                         err = parse_raw_statfile(file, info);
973         } else if (!strncmp(buf, "rc ", 3)) {
974                 /* looks like raw server stats */
975                 if (!is_srv) {
976                         fprintf(stderr, "Warning: no client info present in "
977                                         "raw server stats file.\n");
978                         *opt = 0;
979                 } else
980                         err = parse_raw_statfile(file, info);
981         } else
982                 /* looks like pretty client and server stats */
983                 err = parse_pretty_statfile(file, info);
984 out:
985         if (fp)
986                 fclose(fp);
987         if (err) {
988                 if (!other_opt) {
989                         fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
990                                         label, file);
991                         exit(2);
992                 }
993                 *opt = 0;
994         }
995 }
996
997 /*
998  * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
999  * denotes the number of subsequent entries.  statinfo value arrays contain an additional
1000  * field at the end which contains the sum of all previous elements in the array -- so,
1001  * there are stats if the sum's greater than the entry-count.
1002  */
1003 static int
1004 has_stats(const unsigned int *info)
1005 {
1006         return (info[0] && info[info[0] + 1] > info[0]);
1007 }
1008 static int
1009 has_rpcstats(const unsigned int *info, int size)
1010 {
1011         int i, cnt;
1012
1013         for (i=0, cnt=0; i < size; i++)
1014                 cnt += info[i];
1015         return cnt;
1016 }
1017
1018 /*
1019  * take the difference of each individual stat value in 'new' and 'old'
1020  * and store the results back into 'new'
1021  */
1022 static void
1023 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
1024 {
1025         int i, j, nodiff_first_index, should_diff;
1026
1027         /*
1028          * Different stat types have different formats in the /proc
1029          * files: for the proc2/3/4-type stats, the first entry has
1030          * the total number of subsequent entries; one does not want
1031          * to diff that first entry.  The other stat types aren't like
1032          * this.  So, we diff a given entry if it's not of one of the
1033          * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
1034          * the first entry ("j" > 0).
1035          */
1036         nodiff_first_index = 2 + (2 * is_srv);
1037
1038         for (i = 0; old[i].tag; i++) {
1039                 for (j = 0; j < new[i].nrvals; j++) {
1040                         should_diff = (i < nodiff_first_index || j > 0);
1041                         if (should_diff)
1042                                 new[i].valptr[j] -= old[i].valptr[j];
1043                 }
1044
1045                 /*
1046                  * Make sure that the "totals" entry (last value in
1047                  * each stat array) for the procX-type stats has the
1048                  * "numentries" entry's (first value in procX-type
1049                  * stat arrays) constant value added-back after the
1050                  * diff -- i.e., it should always be included in the
1051                  * total.
1052                  */
1053                 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
1054                         new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1055         }
1056 }
1057
1058 static void
1059 unpause(int sig)
1060 {
1061         double time_diff;
1062         int minutes, seconds;
1063         time_t endtime;
1064
1065         endtime = time(NULL);
1066         time_diff = difftime(endtime, starttime);
1067         minutes = time_diff / 60;
1068         seconds = (int)time_diff % 60;
1069         printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
1070 }
1071
1072 static void
1073 update_old_counters(struct statinfo *new, struct statinfo *old)
1074 {
1075         int z, i;
1076         for (z = 0; old[z].tag; z++) 
1077                 for (i = 0; i <= old[z].nrvals; i++) 
1078                         old[z].valptr[i] += new[z].valptr[i];
1079
1080 }