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