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