]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsstat/nfsstat.c
8f058691d90e2daf4ded6bbaab50c2261b7be9a4
[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];       /* NFSv2 call counts ([0] == 18) */
27 static unsigned int     cltproc2info[20];       /* NFSv2 call counts ([0] == 18) */
28 static unsigned int     srvproc3info[24];       /* NFSv3 call counts ([0] == 22) */
29 static unsigned int     cltproc3info[24];       /* NFSv3 call counts ([0] == 22) */
30 static unsigned int     srvproc4info[4];        /* NFSv4 call counts ([0] == 2) */
31 static unsigned int     cltproc4info[34];       /* NFSv4 call counts ([0] == 32) */
32 static unsigned int     srvproc4opsinfo[42];/* NFSv4 call counts ([0] == 40) */
33 static unsigned int     srvnetinfo[5];  /* 0  # of received packets
34                                          * 1  UDP packets
35                                          * 2  TCP packets
36                                          * 3  TCP connections
37                                          */
38 static unsigned int     cltnetinfo[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];  /* 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];  /* 0  total # of RPC calls
51                                          * 1  retransmitted calls
52                                          * 2  cred refreshs
53                                          */
54
55 static unsigned int     srvrcinfo[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];   /* (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_CLT(cltinfo);
149
150 static void             print_numbers(const char *, unsigned int *,
151                                         unsigned int);
152 static void             print_callstats(const char *, const char **,
153                                         unsigned int *, unsigned int);
154 static int              parse_statfile(const char *, struct statinfo *);
155
156 static statinfo         *get_stat_info(const char *, struct statinfo *);
157
158 static int              mounts(const char *);
159
160 static void             get_stats(const char *, statinfo *, int *, int, const char *);
161 static int              has_stats(const unsigned int *);
162
163 #define PRNT_CALLS      0x0001
164 #define PRNT_RPC        0x0002
165 #define PRNT_NET        0x0004
166 #define PRNT_FH         0x0008
167 #define PRNT_RC         0x0010
168 #define PRNT_AUTO       0x1000
169 #define PRNT_V2         0x2000
170 #define PRNT_V3         0x4000
171 #define PRNT_V4         0x8000
172 #define PRNT_ALL        0x0fff
173
174 int versions[] = {
175         PRNT_V2,
176         PRNT_V3,
177         PRNT_V4
178 };
179
180 void usage(char *name)
181 {
182         printf("Usage: %s [OPTION]...\n\
183 \n\
184   -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
185   -c, --client\t\tShow NFS client statistics\n\
186   -s, --server\t\tShow NFS server statistics\n\
187   -2\t\t\tShow NFS version 2 statistics\n\
188   -3\t\t\tShow NFS version 3 statistics\n\
189   -4\t\t\tShow NFS version 4 statistics\n\
190   -o [facility]\t\tShow statistics on particular facilities.\n\
191      nfs\tNFS protocol information\n\
192      rpc\tGeneral RPC information\n\
193      net\tNetwork layer statistics\n\
194      fh\t\tUsage information on the server's file handle cache\n\
195      rc\t\tUsage information on the server's request reply cache\n\
196      all\tSelect all of the above\n\
197   -v, --verbose, --all\tSame as '-o all'\n\
198   -r, --rpc\t\tShow RPC statistics\n\
199   -n, --nfs\t\tShow NFS statistics\n\
200   --version\t\tShow program version\n\
201   --help\t\tWhat you just did\n\
202 \n", name);
203         exit(0);
204 }
205
206 static struct option longopts[] =
207 {
208         { "acl", 0, 0, 'a' },
209         { "all", 0, 0, 'v' },
210         { "auto", 0, 0, '\3' },
211         { "client", 0, 0, 'c' },
212         { "mounts", 0, 0, 'm' },
213         { "nfs", 0, 0, 'n' },
214         { "rpc", 0, 0, 'r' },
215         { "server", 0, 0, 's' },
216         { "verbose", 0, 0, 'v' },
217         { "zero", 0, 0, 'z' },
218         { "help", 0, 0, '\1' },
219         { "version", 0, 0, '\2' },
220         { NULL, 0, 0, 0 }
221 };
222
223 int
224 main(int argc, char **argv)
225 {
226         int             opt_all = 0,
227                         opt_srv = 0,
228                         opt_clt = 0,
229                         opt_prt = 0;
230         int             c;
231         char           *progname;
232
233         if ((progname = strrchr(argv[0], '/')))
234                 progname++;
235         else
236                 progname = argv[0];
237
238         while ((c = getopt_long(argc, argv, "234acmno:vrsz\1\2", longopts, NULL)) != EOF) {
239                 switch (c) {
240                 case 'a':
241                         fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
242                         return -1;
243                 case 'c':
244                         opt_clt = 1;
245                         break;
246                 case 'n':
247                         opt_prt |= PRNT_CALLS;
248                         break;
249                 case 'o':
250                         if (!strcmp(optarg, "nfs"))
251                                 opt_prt |= PRNT_CALLS;
252                         else if (!strcmp(optarg, "rpc"))
253                                 opt_prt |= PRNT_RPC;
254                         else if (!strcmp(optarg, "net"))
255                                 opt_prt |= PRNT_NET;
256                         else if (!strcmp(optarg, "rc"))
257                                 opt_prt |= PRNT_RC;
258                         else if (!strcmp(optarg, "fh"))
259                                 opt_prt |= PRNT_FH;
260                         else if (!strcmp(optarg, "all"))
261                                 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
262                         else {
263                                 fprintf(stderr, "nfsstat: unknown category: "
264                                                 "%s\n", optarg);
265                                 return 2;
266                         }
267                         break;
268                 case '2':
269                 case '3':
270                 case '4':
271                         opt_prt |= versions[c - '2'];
272                         break;
273                 case 'v':
274                         opt_all = 1;
275                         break;
276                 case '\3':
277                         opt_prt |= PRNT_AUTO;
278                         break;
279                 case 'r':
280                         opt_prt |= PRNT_RPC;
281                         break;
282                 case 's':
283                         opt_srv = 1;
284                         break;
285                 case 'z':
286                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
287                                         "not yet supported\n");
288                         return 2;
289                 case 'm':
290                         return mounts(MOUNTSFILE);
291                 case '\1':
292                         usage(progname);
293                         return 0;
294                 case '\2':
295                         fprintf(stdout, "nfsstat: " VERSION "\n");
296                         return 0;
297                 default:
298                         printf("Try `%s --help' for more information.\n", progname);
299                         return -1;
300                 }
301         }
302
303         if (opt_all) {
304                 opt_srv = opt_clt = 1;
305                 opt_prt |= PRNT_ALL;
306         }
307         if (!(opt_srv + opt_clt))
308                 opt_srv = opt_clt = 1;
309         if (!(opt_prt & 0xfff)) {
310                 opt_prt |= PRNT_CALLS + PRNT_RPC;
311         }
312         if (!(opt_prt & 0xe000)) {
313                 opt_prt |= PRNT_AUTO;
314         }
315         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
316                 fprintf(stderr,
317                         "You requested file handle or request cache "
318                         "statistics while using the -c option.\n"
319                         "This information is available only for the NFS "
320                         "server.\n");
321         }
322
323         if (opt_srv)
324                 get_stats(NFSSRVSTAT, srvinfo, &opt_srv, opt_clt, "Server");
325         if (opt_clt)
326                 get_stats(NFSCLTSTAT, cltinfo, &opt_clt, opt_srv, "Client");
327
328         if (opt_srv) {
329                 if (opt_prt & PRNT_NET) {
330                         print_numbers(
331                         "Server packet stats:\n"
332                         "packets    udp        tcp        tcpconn\n",
333                         srvnetinfo, 4
334                         );
335                         printf("\n");
336                 }
337                 if (opt_prt & PRNT_RPC) {
338                         print_numbers(
339                         "Server rpc stats:\n"
340                         "calls      badcalls   badauth    badclnt    xdrcall\n",
341                         srvrpcinfo, 5
342                         );
343                         printf("\n");
344                 }
345                 if (opt_prt & PRNT_RC) {
346                         print_numbers(
347                         "Server reply cache:\n"
348                         "hits       misses     nocache\n",
349                         srvrcinfo, 3
350                         );
351                         printf("\n");
352                 }
353
354                 /*
355                  * 2.2 puts all fh-related info after the 'rc' header
356                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
357                  *     'stale' to the start and swaps dir and nondir :-(  
358                  *     We preseve the 2.2 order
359                  */
360                 if (opt_prt & PRNT_FH) {
361                         if (get_stat_info("fh", srvinfo)) {     /* >= 2.4 */
362                                 int t = srvfhinfo[3];
363                                 srvfhinfo[3]=srvfhinfo[4];
364                                 srvfhinfo[4]=t;
365                                 
366                                 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
367                                 
368                                 print_numbers(
369                                         "Server file handle cache:\n"
370                                         "lookup     anon       ncachedir  ncachedir  stale\n",
371                                         srvfhinfo + 1, 5);
372                         } else                                  /* < 2.4 */
373                                 print_numbers(
374                                         "Server file handle cache:\n"
375                                         "lookup     anon       ncachedir  ncachedir  stale\n",
376                                         srvrcinfo + 3, 5);
377                         printf("\n");
378                 }
379                 if (opt_prt & PRNT_CALLS) {
380                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
381                                 print_callstats(
382                                 "Server nfs v2:\n",
383                                     nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
384                                 );
385                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
386                                 print_callstats(
387                                 "Server nfs v3:\n",
388                                 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
389                                 );
390                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
391                                 print_callstats(
392                                 "Server nfs v4:\n",
393                                 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
394                                 );
395                                 print_callstats(
396                                 "Server nfs v4 operations:\n",
397                                 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
398                                 );
399                         }
400                 }
401         }
402
403         if (opt_clt) {
404                 if (opt_prt & PRNT_NET) {
405                         print_numbers(
406                         "Client packet stats:\n"
407                         "packets    udp        tcp        tcpconn\n",
408                         cltnetinfo, 4
409                         );
410                         printf("\n");
411                 }
412                 if (opt_prt & PRNT_RPC) {
413                         print_numbers(
414                         "Client rpc stats:\n"
415                         "calls      retrans    authrefrsh\n",
416                         cltrpcinfo, 3
417                         );
418                         printf("\n");
419                 }
420                 if (opt_prt & PRNT_CALLS) {
421                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
422                                 print_callstats(
423                                 "Client nfs v2:\n",
424                                 nfsv2name, cltproc2info + 1,  sizeof(nfsv2name)/sizeof(char *)
425                                 );
426                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
427                                 print_callstats(
428                                 "Client nfs v3:\n",
429                                 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
430                                 );
431                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
432                                 print_callstats(
433                                 "Client nfs v4:\n",
434                                 nfscltproc4name, cltproc4info + 1,  sizeof(nfscltproc4name)/sizeof(char *)
435                                 );
436                 }
437         }
438
439         return 0;
440 }
441
442 static statinfo *
443 get_stat_info(const char *sp, struct statinfo *statp)
444 {
445         struct statinfo *ip;
446
447         for (ip = statp; ip->tag; ip++) {
448                 if (!strcmp(sp, ip->tag))
449                         return ip;
450         }
451
452         return NULL;
453 }
454
455 static void
456 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
457 {
458         unsigned int    i;
459
460         fputs(hdr, stdout);
461         for (i = 0; i < nr; i++)
462                 printf("%s%-8d", i? "   " : "", info[i]);
463         printf("\n");
464 }
465
466 static void
467 print_callstats(const char *hdr, const char **names,
468                                  unsigned int *info, unsigned int nr)
469 {
470         unsigned long long      total;
471         unsigned long long      pct;
472         int             i, j;
473
474         fputs(hdr, stdout);
475         for (i = 0, total = 0; i < nr; i++)
476                 total += info[i];
477         if (!total)
478                 total = 1;
479         for (i = 0; i < nr; i += 6) {
480                 for (j = 0; j < 6 && i + j < nr; j++)
481                         printf("%-13s", names[i+j]);
482                 printf("\n");
483                 for (j = 0; j < 6 && i + j < nr; j++) {
484                         pct = ((unsigned long long) info[i+j]*100)/total;
485                         printf("%-8d%3llu%% ", info[i+j], pct);
486                 }
487                 printf("\n");
488         }
489         printf("\n");
490 }
491
492
493 static int
494 parse_statfile(const char *name, struct statinfo *statp)
495 {
496         char    buffer[4096], *next;
497         FILE    *fp;
498
499         /* Being unable to read e.g. the nfsd stats file shouldn't
500          * be a fatal error -- it usually means the module isn't loaded.
501          */
502         if ((fp = fopen(name, "r")) == NULL) {
503                 // fprintf(stderr, "Warning: %s: %m\n", name);
504                 return 0;
505         }
506
507         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
508                 struct statinfo *ip;
509                 char            *sp, *line = buffer;
510                 unsigned int    i, cnt;
511                 unsigned int    total = 0;
512
513                 if ((next = strchr(line, '\n')) != NULL)
514                         *next++ = '\0';
515                 if (!(sp = strtok(line, " \t")))
516                         continue;
517
518                 ip = get_stat_info(sp, statp);
519                 if (!ip)
520                         continue;
521
522                 cnt = ip->nrvals;
523
524                 for (i = 0; i < cnt; i++) {
525                         if (!(sp = strtok(NULL, " \t")))
526                                 break;
527                         ip->valptr[i] = atoi(sp);
528                         total += ip->valptr[i];
529                 }
530                 ip->valptr[i] = total;
531         }
532
533         fclose(fp);
534         return 1;
535 }
536
537 static int
538 mounts(const char *name)
539 {
540         char    buffer[4096], *next;
541         FILE    *fp;
542
543         /* Being unable to read e.g. the nfsd stats file shouldn't
544          * be a fatal error -- it usually means the module isn't loaded.
545          */
546         if ((fp = fopen(name, "r")) == NULL) {
547                 fprintf(stderr, "Warning: %s: %m\n", name);
548                 return 0;
549         }
550
551         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
552                 char          *line = buffer;
553                 char          *device, *mount, *type, *flags;
554
555                 if ((next = strchr(line, '\n')) != NULL)
556                         *next = '\0';
557
558                 if (!(device = strtok(line, " \t")))
559                         continue;
560
561                 if (!(mount = strtok(NULL, " \t")))
562                         continue;
563
564                 if (!(type = strtok(NULL, " \t")))
565                         continue;
566
567                 if (strcmp(type, "nfs")) {
568                     continue;
569                 }
570
571                 if (!(flags = strtok(NULL, " \t")))
572                         continue;
573
574                 printf("%s from %s\n", mount, device);
575                 printf(" Flags:\t%s\n", flags);
576                 printf("\n");
577
578                 continue;
579         }
580
581         fclose(fp);
582         return 1;
583 }
584
585 static void
586 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
587 {
588         if (!parse_statfile(file, info)) {
589                 if (!other_opt) {
590                         fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
591                         exit(2);
592                 }
593                 *opt = 0;
594         }
595 }
596
597 static int
598 has_stats(const unsigned int *info)
599 {
600         return (info[0] && info[info[0] + 1] != info[0]);
601 }