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