3b695f253612d3ba562a5eb98b5345f53739fb33
[nfs-utils.git] / utils / nfsstat / nfsstat.c
1 /*
2  * nfsstat.c            Output NFS statistics
3  *
4  * Copyright (C) 1995, 1996, 1999 Olaf Kirch <okir@monad.swb.de>
5  */
6
7 #include "config.h"
8
9 #define NFSSVCSTAT      "/proc/net/rpc/nfsd"
10 #define NFSCLTSTAT      "/proc/net/rpc/nfs"
11
12 #define MOUNTSFILE     "/proc/mounts"
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <errno.h>
20
21 #define MAXNRVALS       32
22
23 static unsigned int     svcv2info[19];  /* NFSv2 call counts ([0] == 18) */
24 static unsigned int     cltv2info[19];  /* NFSv2 call counts ([0] == 18) */
25 static unsigned int     svcv3info[23];  /* NFSv3 call counts ([0] == 22) */
26 static unsigned int     cltv3info[23];  /* NFSv3 call counts ([0] == 22) */
27 static unsigned int     svcnetinfo[4];  /* 0  # of received packets
28                                          * 1  UDP packets
29                                          * 2  TCP packets
30                                          * 3  TCP connections
31                                          */
32 static unsigned int     cltnetinfo[4];  /* 0  # of received packets
33                                          * 1  UDP packets
34                                          * 2  TCP packets
35                                          * 3  TCP connections
36                                          */
37
38 static unsigned int     svcrpcinfo[5];  /* 0  total # of RPC calls
39                                          * 1  total # of bad calls
40                                          * 2  bad format
41                                          * 3  authentication failed
42                                          * 4  unknown client
43                                          */
44 static unsigned int     cltrpcinfo[3];  /* 0  total # of RPC calls
45                                          * 1  retransmitted calls
46                                          * 2  cred refreshs
47                                          */
48
49 static unsigned int     svcrcinfo[8];   /* 0  repcache hits
50                                          * 1  repcache hits
51                                          * 2  uncached reqs
52                                          * (for pre-2.4 kernels:)
53                                          * 3  FH lookups
54                                          * 4  'anon' FHs
55                                          * 5  noncached non-directories
56                                          * 6  noncached directories
57                                          * 7  stale
58                                          */
59
60 static unsigned int     svcfhinfo[6];   /* (for kernels >= 2.4.0)
61                                          * 0  stale
62                                          * 1  FH lookups
63                                          * 2  'anon' FHs
64                                          * 3  noncached directories
65                                          * 4  noncached non-directories
66                                          * leave hole to relocate stale for order
67                                          *    compatability.
68                                          */
69
70 static const char *     nfsv2name[18] = {
71         "null", "getattr", "setattr", "root",   "lookup",  "readlink",
72         "read", "wrcache", "write",   "create", "remove",  "rename",
73         "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"
74 };
75
76 static const char *     nfsv3name[22] = {
77         "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
78         "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
79         "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
80         "fsstat", "fsinfo",  "pathconf", "commit"
81 };
82
83 typedef struct statinfo {
84         char            *tag;
85         int             nrvals;
86         unsigned int *  valptr;
87 } statinfo;
88
89 static statinfo         svcinfo[] = {
90         { "net",        4,      svcnetinfo      },
91         { "rpc",        5,      svcrpcinfo      },
92         { "rc",         8,      svcrcinfo       },
93         { "fh",         5,      svcfhinfo       },
94         { "proc2",      19,     svcv2info       },
95         { "proc3",      23,     svcv3info       },
96         { NULL,         0,      0               }
97 };
98
99 static statinfo         cltinfo[] = {
100         { "net",        4,      cltnetinfo      },
101         { "rpc",        3,      cltrpcinfo      },
102         { "proc2",      19,     cltv2info       },
103         { "proc3",      23,     cltv3info       },
104         { NULL,         0,      0               }
105 };
106
107 static void             print_numbers(const char *, unsigned int *,
108                                         unsigned int);
109 static void             print_callstats(const char *, const char **,
110                                         unsigned int *, unsigned int);
111 static int              parse_statfile(const char *, struct statinfo *);
112
113 static statinfo         *get_stat_info(const char *, struct statinfo *);
114
115 static int             mounts(const char *);
116
117 #define PRNT_CALLS      0x0001
118 #define PRNT_RPC        0x0002
119 #define PRNT_NET        0x0004
120 #define PRNT_FH         0x0008
121 #define PRNT_RC         0x0010
122 #define PRNT_ALL        0xffff
123
124 int
125 main(int argc, char **argv)
126 {
127         int             opt_all = 0,
128                         opt_srv = 0,
129                         opt_clt = 0,
130                         srv_info = 0,
131                         clt_info = 0,
132                         opt_prt = 0;
133         int             c;
134
135         while ((c = getopt(argc, argv, "acmno:rsz")) != -1) {
136                 switch (c) {
137                 case 'a':
138                         opt_all = 1;
139                         break;
140                 case 'c':
141                         opt_clt = 1;
142                         break;
143                 case 'n':
144                         opt_prt |= PRNT_CALLS;
145                         break;
146                 case 'o':
147                         if (!strcmp(optarg, "nfs"))
148                                 opt_prt |= PRNT_CALLS;
149                         else if (!strcmp(optarg, "rpc"))
150                                 opt_prt |= PRNT_RPC;
151                         else if (!strcmp(optarg, "net"))
152                                 opt_prt |= PRNT_NET;
153                         else if (!strcmp(optarg, "rc"))
154                                 opt_prt |= PRNT_RC;
155                         else if (!strcmp(optarg, "fh"))
156                                 opt_prt |= PRNT_FH;
157                         else {
158                                 fprintf(stderr, "nfsstat: unknown category: "
159                                                 "%s\n", optarg);
160                                 return 2;
161                         }
162                         break;
163                 case 'r':
164                         opt_prt |= PRNT_RPC;
165                         break;
166                 case 's':
167                         opt_srv = 1;
168                         break;
169                 case 'z':
170                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
171                                         "not yet supported\n");
172                         return 2;
173                 case 'm':
174                         return mounts(MOUNTSFILE);
175                 }
176         }
177
178         if (opt_all) {
179                 opt_srv = opt_clt = 1;
180                 opt_prt = PRNT_ALL;
181         }
182         if (!(opt_srv + opt_clt))
183                 opt_srv = opt_clt = 1;
184         if (!opt_prt)
185                 opt_prt = PRNT_CALLS + PRNT_RPC;
186         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
187                 fprintf(stderr,
188                         "You requested file handle or request cache "
189                         "statistics while using the -c option.\n"
190                         "This information is available only for the NFS "
191                         "server.\n");
192         }
193
194         if (opt_srv) {
195                 srv_info = parse_statfile(NFSSVCSTAT, svcinfo);
196                 if (srv_info == 0 && opt_clt == 0) {
197                         fprintf(stderr, "Warning: No Server Stats (%s: %m).\n", NFSSVCSTAT);
198                         return 2;
199                 }
200                 if (srv_info == 0)
201                         opt_srv = 0;
202         }
203
204         if (opt_clt) {
205                 clt_info = parse_statfile(NFSCLTSTAT, cltinfo);
206                 if (opt_srv == 0 && clt_info == 0) {
207                         fprintf(stderr, "Warning: No Client Stats (%s: %m).\n", NFSCLTSTAT);
208                         return 2;
209                 }
210                 if (clt_info == 0)
211                         opt_clt = 0;
212         }
213
214         if (opt_srv) {
215                 if (opt_prt & PRNT_NET) {
216                         print_numbers(
217                         "Server packet stats:\n"
218                         "packets    udp        tcp        tcpconn\n",
219                         svcnetinfo, 4
220                         );
221                 }
222                 if (opt_prt & PRNT_RPC) {
223                         print_numbers(
224                         "Server rpc stats:\n"
225                         "calls      badcalls   badauth    badclnt    xdrcall\n",
226                         svcrpcinfo, 5
227                         );
228                 }
229                 if (opt_prt & PRNT_RC) {
230                         print_numbers(
231                         "Server reply cache:\n"
232                         "hits       misses     nocache\n",
233                         svcrcinfo, 3
234                         );
235                 }
236
237                 /*
238                  * 2.2 puts all fh-related info after the 'rc' header
239                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
240                  *     'stale' to the start and swaps dir and nondir :-(  
241                  *     We preseve the 2.2 order
242                  */
243                 if (opt_prt & PRNT_FH) {
244                         if (get_stat_info("fh", svcinfo)) {     /* >= 2.4 */
245                                 int t = svcfhinfo[3];
246                                 svcfhinfo[3]=svcfhinfo[4];
247                                 svcfhinfo[4]=t;
248                                 
249                                 svcfhinfo[5]=svcfhinfo[0]; /* relocate 'stale' */
250                                 
251                                 print_numbers(
252                                         "Server file handle cache:\n"
253                                         "lookup     anon       ncachedir ncachedir  stale\n",
254                                         svcfhinfo + 1, 5);
255                         } else                                  /* < 2.4 */
256                                 print_numbers(
257                                         "Server file handle cache:\n"
258                                         "lookup     anon       ncachedir ncachedir  stale\n",
259                                         svcrcinfo + 3, 5);
260                 }
261                 if (opt_prt & PRNT_CALLS) {
262                         print_callstats(
263                         "Server nfs v2:\n",
264                         nfsv2name, svcv2info + 1, 18
265                         );
266                         if (svcv3info[0])
267                                 print_callstats(
268                                 "Server nfs v3:\n",
269                                 nfsv3name, svcv3info + 1, 22
270                                 );
271                 }
272         }
273
274         if (opt_clt) {
275                 if (opt_prt & PRNT_NET) {
276                         print_numbers(
277                         "Client packet stats:\n"
278                         "packets    udp        tcp        tcpconn\n",
279                         cltnetinfo, 4
280                         );
281                 }
282                 if (opt_prt & PRNT_RPC) {
283                         print_numbers(
284                         "Client rpc stats:\n"
285                         "calls      retrans    authrefrsh\n",
286                         cltrpcinfo, 3
287                         );
288                 }
289                 if (opt_prt & PRNT_CALLS) {
290                         print_callstats(
291                         "Client nfs v2:\n",
292                         nfsv2name, cltv2info + 1, 18
293                         );
294                         if (cltv3info[0])
295                                 print_callstats(
296                                 "Client nfs v3:\n",
297                                 nfsv3name, cltv3info + 1, 22
298                                 );
299                 }
300         }
301
302         return 0;
303 }
304
305 static statinfo *
306 get_stat_info(const char *sp, struct statinfo *statp)
307 {
308         struct statinfo *ip;
309
310         for (ip = statp; ip->tag; ip++) {
311                 if (!strcmp(sp, ip->tag))
312                         return ip;
313         }
314
315         return NULL;
316 }
317
318 static void
319 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
320 {
321         unsigned int    i;
322
323         fputs(hdr, stdout);
324         for (i = 0; i < nr; i++)
325                 printf("%s%-8d", i? "   " : "", info[i]);
326         printf("\n");
327 }
328
329 static void
330 print_callstats(const char *hdr, const char **names,
331                                  unsigned int *info, unsigned int nr)
332 {
333         unsigned long long      total;
334         unsigned long long      pct;
335         int             i, j;
336
337         fputs(hdr, stdout);
338         for (i = 0, total = 0; i < nr; i++)
339                 total += info[i];
340         if (!total)
341                 total = 1;
342         for (i = 0; i < nr; i += 6) {
343                 for (j = 0; j < 6 && i + j < nr; j++)
344                         printf("%-11s", names[i+j]);
345                 printf("\n");
346                 for (j = 0; j < 6 && i + j < nr; j++) {
347                         pct = ((unsigned long long) info[i+j]*100)/total;
348                         printf("%-6d %2llu%% ", info[i+j], pct);
349                 }
350                 printf("\n");
351         }
352         printf("\n");
353 }
354
355
356 static int
357 parse_statfile(const char *name, struct statinfo *statp)
358 {
359         char    buffer[4096], *next;
360         FILE    *fp;
361
362         /* Being unable to read e.g. the nfsd stats file shouldn't
363          * be a fatal error -- it usually means the module isn't loaded.
364          */
365         if ((fp = fopen(name, "r")) == NULL) {
366                 // fprintf(stderr, "Warning: %s: %m\n", name);
367                 return 0;
368         }
369
370         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
371                 struct statinfo *ip;
372                 char            *sp, *line = buffer;
373                 int             i, cnt;
374
375                 if ((next = strchr(line, '\n')) != NULL)
376                         *next++ = '\0';
377                 if (!(sp = strtok(line, " \t")))
378                         continue;
379
380                 ip = get_stat_info(sp, statp);
381                 if (!ip)
382                         continue;
383
384                 cnt = ip->nrvals;
385
386                 for (i = 0; i < cnt; i++) {
387                         if (!(sp = strtok(NULL, " \t")))
388                                 break;
389                         ip->valptr[i] = atoi(sp);
390                 }
391         }
392
393         fclose(fp);
394         return 1;
395 }
396
397 static int
398 mounts(const char *name)
399 {
400         char    buffer[4096], *next;
401         FILE    *fp;
402
403         /* Being unable to read e.g. the nfsd stats file shouldn't
404          * be a fatal error -- it usually means the module isn't loaded.
405          */
406         if ((fp = fopen(name, "r")) == NULL) {
407                 fprintf(stderr, "Warning: %s: %m\n", name);
408                 return -1;
409         }
410
411         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
412                 char          *line = buffer;
413                 char          *device, *mount, *type, *flags;
414
415                 if ((next = strchr(line, '\n')) != NULL)
416                         *next = '\0';
417
418                 if (!(device = strtok(line, " \t")))
419                         continue;
420
421                 if (!(mount = strtok(NULL, " \t")))
422                         continue;
423
424                 if (!(type = strtok(NULL, " \t")))
425                         continue;
426
427                 if (strcmp(type, "nfs")) {
428                     continue;
429                 }
430
431                 if (!(flags = strtok(NULL, " \t")))
432                         continue;
433
434                 printf("%s from %s\n", mount, device);
435                 printf(" Flags:\t%s\n", flags);
436                 printf("\n");
437
438                 continue;
439         }
440
441         fclose(fp);
442         return 0;
443 }