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