]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsstat/nfsstat.c
6d81f66c9329ef86935e6a9ad5bcb444f17f7ecb
[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);
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_prt);
440                         } else {
441                                 print_all_stats(opt_srv, opt_clt, opt_prt);
442                         }
443                         fflush(stdout);
444
445                         update_old_counters(clientinfo_tmp, clientinfo);
446                         sleep(sleep_time);
447                 }       
448         } else {
449                 if (opt_list) {
450                         print_stats_list(opt_prt);
451                 } else {
452                         print_all_stats(opt_srv, opt_clt, opt_prt);
453                 }
454         }
455
456         return 0;
457 }
458
459 static void
460 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
461 {
462         print_server_stats(opt_srv, opt_prt);
463         print_client_stats(opt_clt, opt_prt);
464 }
465
466 static void 
467 print_server_stats(int opt_srv, int opt_prt) 
468 {
469         if (!opt_srv)
470                 return;
471
472         if (opt_prt & PRNT_NET) {
473                 if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
474                 } else {
475                         print_numbers( LABEL_srvnet
476                                 "packets    udp        tcp        tcpconn\n",
477                         srvnetinfo, 4);
478                         printf("\n");
479                 }
480         }
481         if (opt_prt & PRNT_RPC) {
482                 if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
483                         ;
484                 } else {
485                         print_numbers(LABEL_srvrpc
486                                 "calls      badcalls   badauth    badclnt    xdrcall\n",
487                                 srvrpcinfo, 5);
488                         printf("\n");
489                 }
490         }
491         if (opt_prt & PRNT_RC) {
492                 if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
493                         ;
494                 } else {
495                         print_numbers(LABEL_srvrc
496                                 "hits       misses     nocache\n",
497                                 srvrcinfo, 3);
498                         printf("\n");
499                 }
500         }
501
502         /*
503          * 2.2 puts all fh-related info after the 'rc' header
504          * 2.4 puts all fh-related info after the 'fh' header, but relocates
505          *     'stale' to the start and swaps dir and nondir :-(  
506          *     We preseve the 2.2 order
507          */
508         if (opt_prt & PRNT_FH) {
509                 if (get_stat_info("fh", srvinfo)) {     /* >= 2.4 */
510                         int t = srvfhinfo[3];
511                         srvfhinfo[3]=srvfhinfo[4];
512                         srvfhinfo[4]=t;
513                         
514                         srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
515                         
516                         print_numbers(
517                                 LABEL_srvfh
518                                 "lookup     anon       ncachedir  ncachedir  stale\n",
519                                 srvfhinfo + 1, 5);
520                 } else                                  /* < 2.4 */
521                         print_numbers(
522                                 LABEL_srvfh
523                                 "lookup     anon       ncachedir  ncachedir  stale\n",
524                                 srvrcinfo + 3, 5);
525                 printf("\n");
526         }
527         if (opt_prt & PRNT_CALLS) {
528                 if ((opt_prt & PRNT_V2) || 
529                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
530                         if (opt_sleep && !has_stats(srvproc2info)) {
531                                 ;
532                         } else {
533                                 print_callstats(LABEL_srvproc2,
534                                         nfsv2name, srvproc2info + 1, 
535                                         sizeof(nfsv2name)/sizeof(char *));
536                         }
537                 }
538                 if ((opt_prt & PRNT_V3) || 
539                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
540                         if (opt_sleep && !has_stats(srvproc3info)) {
541                                 ;
542                         } else {
543                                 print_callstats(LABEL_srvproc3,
544                                         nfsv3name, srvproc3info + 1, 
545                                         sizeof(nfsv3name)/sizeof(char *));
546                         }
547                 }
548                 if ((opt_prt & PRNT_V4) || 
549                                 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
550                         if (opt_sleep && !has_stats(srvproc4info)) {
551                                 ;
552                         } else {
553                                 print_callstats( LABEL_srvproc4,
554                                         nfssrvproc4name, srvproc4info + 1, 
555                                         sizeof(nfssrvproc4name)/sizeof(char *));
556                                 print_callstats(LABEL_srvproc4ops,
557                                         nfssrvproc4opname, srvproc4opsinfo + 1, 
558                                         sizeof(nfssrvproc4opname)/sizeof(char *));
559                         }
560                 }
561         }
562 }
563 static void
564 print_client_stats(int opt_clt, int opt_prt) 
565 {
566         if (!opt_clt)
567                 return;
568
569         if (opt_prt & PRNT_NET) {
570                 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
571                         ;
572                 } else { 
573                         print_numbers(LABEL_cltnet
574                                 "packets    udp        tcp        tcpconn\n",
575                                 cltnetinfo, 4);
576                         printf("\n");
577                 }
578         }
579         if (opt_prt & PRNT_RPC) {
580                 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
581                         ;
582                 } else {
583                         print_numbers(LABEL_cltrpc
584                                 "calls      retrans    authrefrsh\n",
585                                 cltrpcinfo, 3);
586                         printf("\n");
587                 }
588         }
589         if (opt_prt & PRNT_CALLS) {
590                 if ((opt_prt & PRNT_V2) || 
591                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
592                         if (opt_sleep && !has_stats(cltproc2info)) {
593                                 ;
594                         } else {
595                                 print_callstats(LABEL_cltproc2,
596                                         nfsv2name, cltproc2info + 1,  
597                                         sizeof(nfsv2name)/sizeof(char *));
598                         }
599                 }
600                 if ((opt_prt & PRNT_V3) || 
601                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
602                         if (opt_sleep && !has_stats(cltproc3info)) {
603                                 ;
604                         } else {
605                                 print_callstats( LABEL_cltproc3,
606                                         nfsv3name, cltproc3info + 1, 
607                                         sizeof(nfsv3name)/sizeof(char *));
608                         }
609                 }
610                 if ((opt_prt & PRNT_V4) || 
611                                 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
612                         if (opt_sleep && !has_stats(cltproc4info)) {
613                                 ;
614                         } else {
615                                 print_callstats(LABEL_cltproc4,
616                                         nfscltproc4name, cltproc4info + 1,  
617                                         sizeof(nfscltproc4name)/sizeof(char *));
618                         }
619                 }
620         }
621 }
622
623 static void
624 print_stats_list(int opt_prt) 
625 {
626         if (opt_prt & PRNT_CALLS) {
627                 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
628                         print_callstats_list(
629                         "nfs v2 server",
630                         nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *));
631                         print_callstats_list(
632                         "nfs v2 client",
633                         nfsv2name, cltproc2info + 1,  sizeof(nfsv2name)/sizeof(char *));
634                 }
635                 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
636                         print_callstats_list(
637                         "nfs v3 server",
638                         nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *));
639                         print_callstats_list(
640                         "nfs v3 client",
641                         nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *));
642                 }
643                 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
644                         print_callstats_list(
645                         "nfs v4 server",
646                         nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *));
647                         print_callstats_list(
648                         "nfs v4 ops",
649                         nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *));
650                         print_callstats_list(
651                         "nfs v4 client",
652                         nfscltproc4name, cltproc4info + 1,  sizeof(nfscltproc4name)/sizeof(char *));
653                 }
654         }
655 }
656
657 static statinfo *
658 get_stat_info(const char *sp, struct statinfo *statp)
659 {
660         struct statinfo *ip;
661
662         for (ip = statp; ip->tag; ip++) {
663                 if (!strcmp(sp, ip->tag))
664                         return ip;
665         }
666
667         return NULL;
668 }
669
670 static void
671 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
672 {
673         unsigned int    i;
674
675         fputs(hdr, stdout);
676         for (i = 0; i < nr; i++)
677                 printf("%s%-8u", i? "   " : "", info[i]);
678         printf("\n");
679 }
680
681 static void
682 print_callstats(const char *hdr, const char **names,
683                                  unsigned int *info, unsigned int nr)
684 {
685         unsigned long long      total;
686         unsigned long long      pct;
687         int             i, j;
688
689         fputs(hdr, stdout);
690         for (i = 0, total = 0; i < nr; i++)
691                 total += info[i];
692         if (!total)
693                 total = 1;
694         for (i = 0; i < nr; i += 6) {
695                 for (j = 0; j < 6 && i + j < nr; j++)
696                         printf("%-13s", names[i+j]);
697                 printf("\n");
698                 for (j = 0; j < 6 && i + j < nr; j++) {
699                         pct = ((unsigned long long) info[i+j]*100)/total;
700                         printf("%-8u%3llu%% ", info[i+j], pct);
701                 }
702                 printf("\n");
703         }
704         printf("\n");
705 }
706
707 static void
708 print_callstats_list(const char *hdr, const char **names,
709                         unsigned int *callinfo, unsigned int nr)
710 {
711         unsigned long long      calltotal;
712         int                     i;
713
714         for (i = 0, calltotal = 0; i < nr; i++) {
715                 calltotal += callinfo[i];
716         }
717         if (!calltotal)
718                 return;
719         printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
720         printf("------------- ------------- --------\n");
721         for (i = 0; i < nr; i++) {
722                         if (callinfo[i])
723                                 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
724         }
725         printf("\n");
726                 
727 }
728
729
730 /* returns 0 on success, 1 otherwise */
731 static int
732 parse_raw_statfile(const char *name, struct statinfo *statp)
733 {
734         char    buffer[4096], *next;
735         FILE    *fp;
736
737         /* Being unable to read e.g. the nfsd stats file shouldn't
738          * be a fatal error -- it usually means the module isn't loaded.
739          */
740         if ((fp = fopen(name, "r")) == NULL) {
741                 // fprintf(stderr, "Warning: %s: %m\n", name);
742                 return 1;
743         }
744
745         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
746                 struct statinfo *ip;
747                 char            *sp, *line = buffer;
748                 unsigned int    i, cnt;
749                 unsigned int    total = 0;
750
751                 if ((next = strchr(line, '\n')) != NULL)
752                         *next++ = '\0';
753                 if (!(sp = strtok(line, " \t")))
754                         continue;
755
756                 ip = get_stat_info(sp, statp);
757                 if (!ip)
758                         continue;
759
760                 cnt = ip->nrvals;
761
762                 for (i = 0; i < cnt; i++) {
763                         if (!(sp = strtok(NULL, " \t")))
764                                 break;
765                         ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
766                         total += ip->valptr[i];
767                 }
768                 ip->valptr[cnt - 1] = total;
769         }
770
771         fclose(fp);
772         return 0;
773 }
774
775 /* returns 0 on success, 1 otherwise */
776 static int
777 parse_pretty_statfile(const char *filename, struct statinfo *info)
778 {
779         int numvals, curindex, numconsumed, n, err = 1;
780         unsigned int sum;
781         char buf[4096], *bufp, *fmt, is_proc;
782         FILE *fp = NULL;
783         struct statinfo *ip;
784
785         if ((fp = fopen(filename, "r")) == NULL)
786                 //err(2, "Unable to open statfile '%s'.\n", filename);
787                 goto out;
788
789         while (fgets(buf, sizeof(buf), fp) != NULL) {
790                 for (ip = info; ip->tag; ip++) {
791                         if (strcmp(buf, ip->label))
792                                 continue;
793
794                         sum = 0;
795                         numvals = ip->nrvals - 1;
796                         is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
797                         if (is_proc) {
798                                 fmt = " %u %*u%% %n";
799                                 curindex = 1;
800                                 ip->valptr[0] = 0;
801                         } else {
802                                 fmt = " %u %n";
803                                 curindex = 0;
804                         }
805 more_stats:
806                         /* get (and skip) header */
807                         if (fgets(buf, sizeof(buf), fp) == NULL) {
808                                 fprintf(stderr, "Failed to locate header after "
809                                                 "label for '%s' in %s.\n",
810                                                 ip->tag, filename);
811                                 goto out;
812                         }
813                         /* no header -- done with this "tag" */
814                         if (*buf == '\n') {
815                                 ip->valptr[numvals] = sum;
816                                 break;
817                         }
818                         /* get stats */
819                         if (fgets(buf, sizeof(buf), fp) == NULL) {
820                                 fprintf(stderr, "Failed to locate stats after "
821                                                 "header for '%s' in %s.\n",
822                                                 ip->tag, filename);
823                                 goto out;
824                         }
825                         bufp = buf;
826                         for (; curindex < numvals; curindex++) {
827                                 n = sscanf(bufp, fmt, &ip->valptr[curindex],
828                                                 &numconsumed);
829                                 if (n != 1)
830                                         break;
831                                 if (is_proc) {
832                                         ip->valptr[0]++;
833                                         sum++;
834                                 }
835                                 sum += ip->valptr[curindex];
836                                 bufp += numconsumed;
837                         }
838                         goto more_stats;
839                 }
840         }
841         err = 0;
842 out:
843         if (fp)
844                 fclose(fp);
845         return err;
846 }
847
848 static int
849 mounts(const char *name)
850 {
851         char    buffer[4096], *next;
852         FILE    *fp;
853
854         /* Being unable to read e.g. the nfsd stats file shouldn't
855          * be a fatal error -- it usually means the module isn't loaded.
856          */
857         if ((fp = fopen(name, "r")) == NULL) {
858                 fprintf(stderr, "Warning: %s: %m\n", name);
859                 return 0;
860         }
861
862         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
863                 char          *line = buffer;
864                 char          *device, *mount, *type, *flags;
865
866                 if ((next = strchr(line, '\n')) != NULL)
867                         *next = '\0';
868
869                 if (!(device = strtok(line, " \t")))
870                         continue;
871
872                 if (!(mount = strtok(NULL, " \t")))
873                         continue;
874
875                 if (!(type = strtok(NULL, " \t")))
876                         continue;
877
878                 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
879                     continue;
880                 }
881
882                 if (!(flags = strtok(NULL, " \t")))
883                         continue;
884
885                 printf("%s from %s\n", mount, device);
886                 printf(" Flags:\t%s\n", flags);
887                 printf("\n");
888
889                 continue;
890         }
891
892         fclose(fp);
893         return 1;
894 }
895
896 static void
897 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
898                 int is_srv)
899 {
900         FILE *fp;
901         char buf[10];
902         int err = 1;
903         char *label = is_srv ? "Server" : "Client";
904
905         /* try to guess what type of stat file we're dealing with */
906         if ((fp = fopen(file, "r")) == NULL)
907                 goto out;
908         if (fgets(buf, 10, fp) == NULL)
909                 goto out;
910         if (!strncmp(buf, "net ", 4)) {
911                 /* looks like raw client stats */
912                 if (is_srv) {
913                         fprintf(stderr, "Warning: no server info present in "
914                                         "raw client stats file.\n");
915                         *opt = 0;
916                 } else
917                         err = parse_raw_statfile(file, info);
918         } else if (!strncmp(buf, "rc ", 3)) {
919                 /* looks like raw server stats */
920                 if (!is_srv) {
921                         fprintf(stderr, "Warning: no client info present in "
922                                         "raw server stats file.\n");
923                         *opt = 0;
924                 } else
925                         err = parse_raw_statfile(file, info);
926         } else
927                 /* looks like pretty client and server stats */
928                 err = parse_pretty_statfile(file, info);
929 out:
930         if (fp)
931                 fclose(fp);
932         if (err) {
933                 if (!other_opt) {
934                         fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
935                                         label, file);
936                         exit(2);
937                 }
938                 *opt = 0;
939         }
940 }
941
942 /*
943  * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
944  * denotes the number of subsequent entries.  statinfo value arrays contain an additional
945  * field at the end which contains the sum of all previous elements in the array -- so,
946  * there are stats if the sum's greater than the entry-count.
947  */
948 static int
949 has_stats(const unsigned int *info)
950 {
951         return (info[0] && info[info[0] + 1] > info[0]);
952 }
953 static int
954 has_rpcstats(const unsigned int *info, int size)
955 {
956         int i, cnt;
957
958         for (i=0, cnt=0; i < size; i++)
959                 cnt += info[i];
960         return cnt;
961 }
962
963 /*
964  * take the difference of each individual stat value in 'new' and 'old'
965  * and store the results back into 'new'
966  */
967 static void
968 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
969 {
970         int i, j, nodiff_first_index, should_diff;
971
972         /*
973          * Different stat types have different formats in the /proc
974          * files: for the proc2/3/4-type stats, the first entry has
975          * the total number of subsequent entries; one does not want
976          * to diff that first entry.  The other stat types aren't like
977          * this.  So, we diff a given entry if it's not of one of the
978          * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
979          * the first entry ("j" > 0).
980          */
981         nodiff_first_index = 2 + (2 * is_srv);
982
983         for (i = 0; old[i].tag; i++) {
984                 for (j = 0; j < new[i].nrvals; j++) {
985                         should_diff = (i < nodiff_first_index || j > 0);
986                         if (should_diff)
987                                 new[i].valptr[j] -= old[i].valptr[j];
988                 }
989
990                 /*
991                  * Make sure that the "totals" entry (last value in
992                  * each stat array) for the procX-type stats has the
993                  * "numentries" entry's (first value in procX-type
994                  * stat arrays) constant value added-back after the
995                  * diff -- i.e., it should always be included in the
996                  * total.
997                  */
998                 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
999                         new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1000         }
1001 }
1002
1003 static void
1004 unpause(int sig)
1005 {
1006         double time_diff;
1007         int minutes, seconds;
1008         time_t endtime;
1009
1010         endtime = time(NULL);
1011         time_diff = difftime(endtime, starttime);
1012         minutes = time_diff / 60;
1013         seconds = (int)time_diff % 60;
1014         printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
1015 }
1016
1017 static void
1018 update_old_counters(struct statinfo *new, struct statinfo *old)
1019 {
1020         int z, i;
1021         for (z = 0; old[z].tag; z++) 
1022                 for (i = 0; i <= old[z].nrvals; i++) 
1023                         old[z].valptr[i] += new[z].valptr[i];
1024
1025 }