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