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