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