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