]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsstat/nfsstat.c
bd105f5d7b1b5d056cbec13966f0ccaaf38c1326
[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
24 #define MAXNRVALS       32
25
26 static unsigned int     srvproc2info[20], srvproc2info_tmp[20]; /* NFSv2 call counts ([0] == 18) */
27 static unsigned int     cltproc2info[20], cltproc2info_tmp[20]; /* NFSv2 call counts ([0] == 18) */
28 static unsigned int     srvproc3info[24], srvproc3info_tmp[24]; /* NFSv3 call counts ([0] == 22) */
29 static unsigned int     cltproc3info[24], cltproc3info_tmp[24]; /* NFSv3 call counts ([0] == 22) */
30 static unsigned int     srvproc4info[4], srvproc4info_tmp[4];   /* NFSv4 call counts ([0] == 2) */
31 static unsigned int     cltproc4info[34], cltproc4info_tmp[34]; /* NFSv4 call counts ([0] == 32) */
32 static unsigned int     srvproc4opsinfo[42], srvproc4opsinfo_tmp[42];   /* NFSv4 call counts ([0] == 40) */
33 static unsigned int     srvnetinfo[5], srvnetinfo_tmp[5];       /* 0  # of received packets
34                                                                  * 1  UDP packets
35                                                                  * 2  TCP packets
36                                                                  * 3  TCP connections
37                                                                  */
38 static unsigned int     cltnetinfo[5], cltnetinfo_tmp[5];       /* 0  # of received packets
39                                                                  * 1  UDP packets
40                                                                  * 2  TCP packets
41                                                                  * 3  TCP connections
42                                                                  */
43
44 static unsigned int     srvrpcinfo[6], srvrpcinfo_tmp[6];       /* 0  total # of RPC calls
45                                                                  * 1  total # of bad calls
46                                                                  * 2  bad format
47                                                                  * 3  authentication failed
48                                                                  * 4  unknown client
49                                                                  */
50 static unsigned int     cltrpcinfo[4], cltrpcinfo_tmp[4];       /* 0  total # of RPC calls
51                                                                  * 1  retransmitted calls
52                                                                  * 2  cred refreshs
53                                                                  */
54
55 static unsigned int     srvrcinfo[9], srvrcinfo_tmp[9];         /* 0  repcache hits
56                                                                  * 1  repcache hits
57                                                                  * 2  uncached reqs
58                                                                  * (for pre-2.4 kernels:)
59                                                                  * 3  FH lookups
60                                                                  * 4  'anon' FHs
61                                                                  * 5  noncached non-directories
62                                                                  * 6  noncached directories
63                                                                  * 7  stale
64                                                                  */
65
66 static unsigned int     srvfhinfo[7], srvfhinfo_tmp[7];         /* (for kernels >= 2.4.0)
67                                                                  * 0  stale
68                                                                  * 1  FH lookups
69                                                                  * 2  'anon' FHs
70                                                                  * 3  noncached directories
71                                                                  * 4  noncached non-directories
72                                                                  * leave hole to relocate stale for order
73                                                                  *    compatability.
74                                                                  */
75
76 static const char *     nfsv2name[18] = {
77         "null", "getattr", "setattr", "root",   "lookup",  "readlink",
78         "read", "wrcache", "write",   "create", "remove",  "rename",
79         "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"
80 };
81
82 static const char *     nfsv3name[22] = {
83         "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
84         "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
85         "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
86         "fsstat", "fsinfo",  "pathconf", "commit"
87 };
88
89 static const char *     nfssrvproc4name[2] = {
90         "null",
91         "compound",
92 };
93
94 static const char *     nfscltproc4name[32] = {
95         "null",      "read",      "write",   "commit",      "open",        "open_conf",
96         "open_noat", "open_dgrd", "close",   "setattr",     "fsinfo",      "renew",
97         "setclntid", "confirm",   "lock",
98         "lockt",     "locku",     "access",  "getattr",     "lookup",      "lookup_root",
99         "remove",    "rename",    "link",    "symlink",     "create",      "pathconf",
100         "statfs",    "readlink",  "readdir", "server_caps", "delegreturn",
101 };
102
103 static const char *     nfssrvproc4opname[40] = {
104         "op0-unused",   "op1-unused", "op2-future",  "access",     "close",       "commit",
105         "create",       "delegpurge", "delegreturn", "getattr",    "getfh",       "link",
106         "lock",         "lockt",      "locku",       "lookup",     "lookup_root", "nverify",
107         "open",         "openattr",   "open_conf",   "open_dgrd",  "putfh",       "putpubfh",
108         "putrootfh",    "read",       "readdir",     "readlink",   "remove",      "rename",
109         "renew",        "restorefh",  "savefh",      "secinfo",    "setattr",     "setcltid",
110         "setcltidconf", "verify",     "write",       "rellockowner"
111 };
112
113 typedef struct statinfo {
114         char            *tag;
115         int             nrvals;
116         unsigned int *  valptr;
117 } statinfo;
118
119 /*
120  * We now build the arrays of statinfos using macros, which will make it easier
121  * to add new variables for --diff-stat.
122  * e.g., SRV(net) expands into the struct statinfo:  { "net", 5, srvnetinfo }
123  */
124 #define ARRAYSIZE(x)            sizeof(x)/sizeof(*x)
125 #define STATINFO(k, t, s...)    { #t, ARRAYSIZE(k##t##info##s), k##t##info##s }
126 #define SRV(t, s...)            STATINFO(srv, t, s)
127 #define CLT(t, s...)            STATINFO(clt, t, s)
128 #define DECLARE_SRV(n, s...)    static statinfo n##s[] = { \
129                                         SRV(net,s), \
130                                         SRV(rpc,s), \
131                                         SRV(rc,s), \
132                                         SRV(fh,s), \
133                                         SRV(proc2,s), \
134                                         SRV(proc3,s),\
135                                         SRV(proc4,s), \
136                                         SRV(proc4ops,s),\
137                                         { NULL, 0, NULL }\
138                                 }
139 #define DECLARE_CLT(n, s...)    static statinfo n##s[] = { \
140                                         CLT(net,s), \
141                                         CLT(rpc,s), \
142                                         CLT(proc2,s),\
143                                         CLT(proc3,s), \
144                                         CLT(proc4,s),\
145                                         { NULL, 0, NULL }\
146                                 }
147 DECLARE_SRV(srvinfo);
148 DECLARE_SRV(srvinfo, _tmp);
149 DECLARE_CLT(cltinfo);
150 DECLARE_CLT(cltinfo, _tmp);
151
152 static void             print_numbers(const char *, unsigned int *,
153                                         unsigned int);
154 static void             print_callstats(const char *, const char **,
155                                         unsigned int *, unsigned int);
156 static int              parse_statfile(const char *, struct statinfo *);
157
158 static statinfo         *get_stat_info(const char *, struct statinfo *);
159
160 static int              mounts(const char *);
161
162 static void             get_stats(const char *, statinfo *, int *, int, const char *);
163 static int              has_stats(const unsigned int *);
164
165 #define PRNT_CALLS      0x0001
166 #define PRNT_RPC        0x0002
167 #define PRNT_NET        0x0004
168 #define PRNT_FH         0x0008
169 #define PRNT_RC         0x0010
170 #define PRNT_AUTO       0x1000
171 #define PRNT_V2         0x2000
172 #define PRNT_V3         0x4000
173 #define PRNT_V4         0x8000
174 #define PRNT_ALL        0x0fff
175
176 int versions[] = {
177         PRNT_V2,
178         PRNT_V3,
179         PRNT_V4
180 };
181
182 void usage(char *name)
183 {
184         printf("Usage: %s [OPTION]...\n\
185 \n\
186   -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
187   -c, --client\t\tShow NFS client statistics\n\
188   -s, --server\t\tShow NFS server statistics\n\
189   -2\t\t\tShow NFS version 2 statistics\n\
190   -3\t\t\tShow NFS version 3 statistics\n\
191   -4\t\t\tShow NFS version 4 statistics\n\
192   -o [facility]\t\tShow statistics on particular facilities.\n\
193      nfs\tNFS protocol information\n\
194      rpc\tGeneral RPC information\n\
195      net\tNetwork layer statistics\n\
196      fh\t\tUsage information on the server's file handle cache\n\
197      rc\t\tUsage information on the server's request reply cache\n\
198      all\tSelect all of the above\n\
199   -v, --verbose, --all\tSame as '-o all'\n\
200   -r, --rpc\t\tShow RPC statistics\n\
201   -n, --nfs\t\tShow NFS statistics\n\
202   --version\t\tShow program version\n\
203   --help\t\tWhat you just did\n\
204 \n", name);
205         exit(0);
206 }
207
208 static struct option longopts[] =
209 {
210         { "acl", 0, 0, 'a' },
211         { "all", 0, 0, 'v' },
212         { "auto", 0, 0, '\3' },
213         { "client", 0, 0, 'c' },
214         { "mounts", 0, 0, 'm' },
215         { "nfs", 0, 0, 'n' },
216         { "rpc", 0, 0, 'r' },
217         { "server", 0, 0, 's' },
218         { "verbose", 0, 0, 'v' },
219         { "zero", 0, 0, 'z' },
220         { "help", 0, 0, '\1' },
221         { "version", 0, 0, '\2' },
222         { NULL, 0, 0, 0 }
223 };
224
225 int
226 main(int argc, char **argv)
227 {
228         int             opt_all = 0,
229                         opt_srv = 0,
230                         opt_clt = 0,
231                         opt_prt = 0;
232         int             c;
233         char           *progname;
234
235         if ((progname = strrchr(argv[0], '/')))
236                 progname++;
237         else
238                 progname = argv[0];
239
240         while ((c = getopt_long(argc, argv, "234acmno:vrsz\1\2", longopts, NULL)) != EOF) {
241                 switch (c) {
242                 case 'a':
243                         fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
244                         return -1;
245                 case 'c':
246                         opt_clt = 1;
247                         break;
248                 case 'n':
249                         opt_prt |= PRNT_CALLS;
250                         break;
251                 case 'o':
252                         if (!strcmp(optarg, "nfs"))
253                                 opt_prt |= PRNT_CALLS;
254                         else if (!strcmp(optarg, "rpc"))
255                                 opt_prt |= PRNT_RPC;
256                         else if (!strcmp(optarg, "net"))
257                                 opt_prt |= PRNT_NET;
258                         else if (!strcmp(optarg, "rc"))
259                                 opt_prt |= PRNT_RC;
260                         else if (!strcmp(optarg, "fh"))
261                                 opt_prt |= PRNT_FH;
262                         else if (!strcmp(optarg, "all"))
263                                 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
264                         else {
265                                 fprintf(stderr, "nfsstat: unknown category: "
266                                                 "%s\n", optarg);
267                                 return 2;
268                         }
269                         break;
270                 case '2':
271                 case '3':
272                 case '4':
273                         opt_prt |= versions[c - '2'];
274                         break;
275                 case 'v':
276                         opt_all = 1;
277                         break;
278                 case '\3':
279                         opt_prt |= PRNT_AUTO;
280                         break;
281                 case 'r':
282                         opt_prt |= PRNT_RPC;
283                         break;
284                 case 's':
285                         opt_srv = 1;
286                         break;
287                 case 'z':
288                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
289                                         "not yet supported\n");
290                         return 2;
291                 case 'm':
292                         return mounts(MOUNTSFILE);
293                 case '\1':
294                         usage(progname);
295                         return 0;
296                 case '\2':
297                         fprintf(stdout, "nfsstat: " VERSION "\n");
298                         return 0;
299                 default:
300                         printf("Try `%s --help' for more information.\n", progname);
301                         return -1;
302                 }
303         }
304
305         if (opt_all) {
306                 opt_srv = opt_clt = 1;
307                 opt_prt |= PRNT_ALL;
308         }
309         if (!(opt_srv + opt_clt))
310                 opt_srv = opt_clt = 1;
311         if (!(opt_prt & 0xfff)) {
312                 opt_prt |= PRNT_CALLS + PRNT_RPC;
313         }
314         if (!(opt_prt & 0xe000)) {
315                 opt_prt |= PRNT_AUTO;
316         }
317         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
318                 fprintf(stderr,
319                         "You requested file handle or request cache "
320                         "statistics while using the -c option.\n"
321                         "This information is available only for the NFS "
322                         "server.\n");
323         }
324
325         if (opt_srv)
326                 get_stats(NFSSRVSTAT, srvinfo, &opt_srv, opt_clt, "Server");
327         if (opt_clt)
328                 get_stats(NFSCLTSTAT, cltinfo, &opt_clt, opt_srv, "Client");
329
330         if (opt_srv) {
331                 if (opt_prt & PRNT_NET) {
332                         print_numbers(
333                         "Server packet stats:\n"
334                         "packets    udp        tcp        tcpconn\n",
335                         srvnetinfo, 4
336                         );
337                         printf("\n");
338                 }
339                 if (opt_prt & PRNT_RPC) {
340                         print_numbers(
341                         "Server rpc stats:\n"
342                         "calls      badcalls   badauth    badclnt    xdrcall\n",
343                         srvrpcinfo, 5
344                         );
345                         printf("\n");
346                 }
347                 if (opt_prt & PRNT_RC) {
348                         print_numbers(
349                         "Server reply cache:\n"
350                         "hits       misses     nocache\n",
351                         srvrcinfo, 3
352                         );
353                         printf("\n");
354                 }
355
356                 /*
357                  * 2.2 puts all fh-related info after the 'rc' header
358                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
359                  *     'stale' to the start and swaps dir and nondir :-(  
360                  *     We preseve the 2.2 order
361                  */
362                 if (opt_prt & PRNT_FH) {
363                         if (get_stat_info("fh", srvinfo)) {     /* >= 2.4 */
364                                 int t = srvfhinfo[3];
365                                 srvfhinfo[3]=srvfhinfo[4];
366                                 srvfhinfo[4]=t;
367                                 
368                                 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
369                                 
370                                 print_numbers(
371                                         "Server file handle cache:\n"
372                                         "lookup     anon       ncachedir  ncachedir  stale\n",
373                                         srvfhinfo + 1, 5);
374                         } else                                  /* < 2.4 */
375                                 print_numbers(
376                                         "Server file handle cache:\n"
377                                         "lookup     anon       ncachedir  ncachedir  stale\n",
378                                         srvrcinfo + 3, 5);
379                         printf("\n");
380                 }
381                 if (opt_prt & PRNT_CALLS) {
382                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
383                                 print_callstats(
384                                 "Server nfs v2:\n",
385                                     nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
386                                 );
387                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
388                                 print_callstats(
389                                 "Server nfs v3:\n",
390                                 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
391                                 );
392                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
393                                 print_callstats(
394                                 "Server nfs v4:\n",
395                                 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
396                                 );
397                                 print_callstats(
398                                 "Server nfs v4 operations:\n",
399                                 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
400                                 );
401                         }
402                 }
403         }
404
405         if (opt_clt) {
406                 if (opt_prt & PRNT_NET) {
407                         print_numbers(
408                         "Client packet stats:\n"
409                         "packets    udp        tcp        tcpconn\n",
410                         cltnetinfo, 4
411                         );
412                         printf("\n");
413                 }
414                 if (opt_prt & PRNT_RPC) {
415                         print_numbers(
416                         "Client rpc stats:\n"
417                         "calls      retrans    authrefrsh\n",
418                         cltrpcinfo, 3
419                         );
420                         printf("\n");
421                 }
422                 if (opt_prt & PRNT_CALLS) {
423                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
424                                 print_callstats(
425                                 "Client nfs v2:\n",
426                                 nfsv2name, cltproc2info + 1,  sizeof(nfsv2name)/sizeof(char *)
427                                 );
428                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
429                                 print_callstats(
430                                 "Client nfs v3:\n",
431                                 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
432                                 );
433                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
434                                 print_callstats(
435                                 "Client nfs v4:\n",
436                                 nfscltproc4name, cltproc4info + 1,  sizeof(nfscltproc4name)/sizeof(char *)
437                                 );
438                 }
439         }
440
441         return 0;
442 }
443
444 static statinfo *
445 get_stat_info(const char *sp, struct statinfo *statp)
446 {
447         struct statinfo *ip;
448
449         for (ip = statp; ip->tag; ip++) {
450                 if (!strcmp(sp, ip->tag))
451                         return ip;
452         }
453
454         return NULL;
455 }
456
457 static void
458 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
459 {
460         unsigned int    i;
461
462         fputs(hdr, stdout);
463         for (i = 0; i < nr; i++)
464                 printf("%s%-8d", i? "   " : "", info[i]);
465         printf("\n");
466 }
467
468 static void
469 print_callstats(const char *hdr, const char **names,
470                                  unsigned int *info, unsigned int nr)
471 {
472         unsigned long long      total;
473         unsigned long long      pct;
474         int             i, j;
475
476         fputs(hdr, stdout);
477         for (i = 0, total = 0; i < nr; i++)
478                 total += info[i];
479         if (!total)
480                 total = 1;
481         for (i = 0; i < nr; i += 6) {
482                 for (j = 0; j < 6 && i + j < nr; j++)
483                         printf("%-13s", names[i+j]);
484                 printf("\n");
485                 for (j = 0; j < 6 && i + j < nr; j++) {
486                         pct = ((unsigned long long) info[i+j]*100)/total;
487                         printf("%-8d%3llu%% ", info[i+j], pct);
488                 }
489                 printf("\n");
490         }
491         printf("\n");
492 }
493
494
495 static int
496 parse_statfile(const char *name, struct statinfo *statp)
497 {
498         char    buffer[4096], *next;
499         FILE    *fp;
500
501         /* Being unable to read e.g. the nfsd stats file shouldn't
502          * be a fatal error -- it usually means the module isn't loaded.
503          */
504         if ((fp = fopen(name, "r")) == NULL) {
505                 // fprintf(stderr, "Warning: %s: %m\n", name);
506                 return 0;
507         }
508
509         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
510                 struct statinfo *ip;
511                 char            *sp, *line = buffer;
512                 unsigned int    i, cnt;
513                 unsigned int    total = 0;
514
515                 if ((next = strchr(line, '\n')) != NULL)
516                         *next++ = '\0';
517                 if (!(sp = strtok(line, " \t")))
518                         continue;
519
520                 ip = get_stat_info(sp, statp);
521                 if (!ip)
522                         continue;
523
524                 cnt = ip->nrvals;
525
526                 for (i = 0; i < cnt; i++) {
527                         if (!(sp = strtok(NULL, " \t")))
528                                 break;
529                         ip->valptr[i] = atoi(sp);
530                         total += ip->valptr[i];
531                 }
532                 ip->valptr[i] = total;
533         }
534
535         fclose(fp);
536         return 1;
537 }
538
539 static int
540 mounts(const char *name)
541 {
542         char    buffer[4096], *next;
543         FILE    *fp;
544
545         /* Being unable to read e.g. the nfsd stats file shouldn't
546          * be a fatal error -- it usually means the module isn't loaded.
547          */
548         if ((fp = fopen(name, "r")) == NULL) {
549                 fprintf(stderr, "Warning: %s: %m\n", name);
550                 return 0;
551         }
552
553         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
554                 char          *line = buffer;
555                 char          *device, *mount, *type, *flags;
556
557                 if ((next = strchr(line, '\n')) != NULL)
558                         *next = '\0';
559
560                 if (!(device = strtok(line, " \t")))
561                         continue;
562
563                 if (!(mount = strtok(NULL, " \t")))
564                         continue;
565
566                 if (!(type = strtok(NULL, " \t")))
567                         continue;
568
569                 if (strcmp(type, "nfs")) {
570                     continue;
571                 }
572
573                 if (!(flags = strtok(NULL, " \t")))
574                         continue;
575
576                 printf("%s from %s\n", mount, device);
577                 printf(" Flags:\t%s\n", flags);
578                 printf("\n");
579
580                 continue;
581         }
582
583         fclose(fp);
584         return 1;
585 }
586
587 static void
588 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
589 {
590         if (!parse_statfile(file, info)) {
591                 if (!other_opt) {
592                         fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
593                         exit(2);
594                 }
595                 *opt = 0;
596         }
597 }
598
599 static int
600 has_stats(const unsigned int *info)
601 {
602         return (info[0] && info[info[0] + 1] != info[0]);
603 }