Initial revision
[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[22];  /* NFSv3 call counts ([0] == 22) */
24 static unsigned int     cltv3info[22];  /* 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                                          *
51                                          * including fh info:
52                                          * 3  cached fh's
53                                          * 4  valid fh's
54                                          * 5  fixup required
55                                          * 6  lookup (?)
56                                          * 7  stale
57                                          */
58
59 static const char *     nfsv2name[18] = {
60         "null", "getattr", "setattr", "root",   "lookup",  "readlink",
61         "read", "wrcache", "write",   "create", "remove",  "rename",
62         "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"
63 };
64
65 static const char *     nfsv3name[22] = {
66         "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
67         "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
68         "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
69         "fsstat", "fsinfo",  "pathconf", "commit"
70 };
71
72 typedef struct statinfo {
73         char            *tag;
74         int             nrvals;
75         unsigned int *  valptr;
76
77         /* Filled in by parse_statfile */
78         int *           foundp;
79 } statinfo;
80
81 static statinfo         svcinfo[] = {
82         { "net",        4,      svcnetinfo      },
83         { "rpc",        5,      svcrpcinfo      },
84         { "rc",         8,      svcrcinfo       },      /* including fh_* */
85         { "proc2",      19,     svcv2info       },
86         { "proc3",      23,     svcv3info       },
87         { NULL,         0,      0               }
88 };
89
90 static statinfo         cltinfo[] = {
91         { "net",        4,      cltnetinfo      },
92         { "rpc",        3,      cltrpcinfo      },
93         { "proc2",      19,     cltv2info       },
94         { "proc3",      23,     cltv3info       },
95         { NULL,         0,      0               }
96 };
97
98 static void             print_numbers(const char *, unsigned int *,
99                                         unsigned int);
100 static void             print_callstats(const char *, const char **,
101                                         unsigned int *, unsigned int);
102 static int              parse_statfile(const char *, struct statinfo *);
103
104 #define PRNT_CALLS      0x0001
105 #define PRNT_RPC        0x0002
106 #define PRNT_NET        0x0004
107 #define PRNT_FH         0x0008
108 #define PRNT_RC         0x0010
109 #define PRNT_ALL        0xffff
110
111 int
112 main(int argc, char **argv)
113 {
114         int             opt_all = 0,
115                         opt_srv = 0,
116                         opt_clt = 0,
117                         opt_prt = 0;
118         int             c;
119
120         while ((c = getopt(argc, argv, "acno:rsz")) != -1) {
121                 switch (c) {
122                 case 'a':
123                         opt_all = 1;
124                         break;
125                 case 'c':
126                         opt_clt = 1;
127                         break;
128                 case 'n':
129                         opt_prt |= PRNT_CALLS;
130                         break;
131                 case 'o':
132                         if (!strcmp(optarg, "nfs"))
133                                 opt_prt |= PRNT_CALLS;
134                         else if (!strcmp(optarg, "rpc"))
135                                 opt_prt |= PRNT_RPC;
136                         else if (!strcmp(optarg, "net"))
137                                 opt_prt |= PRNT_NET;
138                         else if (!strcmp(optarg, "rc"))
139                                 opt_prt |= PRNT_RC;
140                         else if (!strcmp(optarg, "fh"))
141                                 opt_prt |= PRNT_FH;
142                         else {
143                                 fprintf(stderr, "nfsstat: unknown category: "
144                                                 "%s\n", optarg);
145                                 return 2;
146                         }
147                         break;
148                 case 'r':
149                         opt_prt |= PRNT_RPC;
150                         break;
151                 case 's':
152                         opt_srv = 1;
153                         break;
154                 case 'z':
155                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
156                                         "not yet supported\n");
157                         return 2;
158                 }
159         }
160
161         if (opt_all) {
162                 opt_srv = opt_clt = 1;
163                 opt_prt = PRNT_ALL;
164         }
165         if (!(opt_srv + opt_clt))
166                 opt_srv = opt_clt = 1;
167         if (!opt_prt)
168                 opt_prt = PRNT_CALLS + PRNT_RPC;
169         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
170                 fprintf(stderr,
171                         "You requested file handle or request cache "
172                         "statistics while using the -c option.\n"
173                         "This information is available only for the NFS "
174                         "server.\n");
175         }
176
177         if ((opt_srv && !parse_statfile(NFSSVCSTAT, svcinfo))
178          || (opt_clt && !parse_statfile(NFSCLTSTAT, cltinfo)))
179                 return 2;
180
181         if (opt_srv) {
182                 if (opt_prt & PRNT_NET) {
183                         print_numbers(
184                         "Server packet stats:\n"
185                         "packets    udp        tcp        tcpconn\n",
186                         svcnetinfo, 4
187                         );
188                 }
189                 if (opt_prt & PRNT_RPC) {
190                         print_numbers(
191                         "Server rpc stats:\n"
192                         "calls      badcalls   badauth    badclnt    xdrcall\n",
193                         svcrpcinfo, 5
194                         );
195                 }
196                 if (opt_prt & PRNT_RC) {
197                         print_numbers(
198                         "Server reply cache:\n"
199                         "hits       misses     nocache\n",
200                         svcrcinfo, 3
201                         );
202                 }
203                 if (opt_prt & PRNT_FH) {
204                         print_numbers(
205                         "Server file handle cache:\n"
206                         "cached     valid      fixup      lookup     stale\n",
207                         svcrcinfo + 3, 5);
208                 }
209                 if (opt_prt & PRNT_CALLS) {
210                         print_callstats(
211                         "Server nfs v2:\n",
212                         nfsv2name, svcv2info + 1, 18
213                         );
214                         if (svcv3info[0])
215                                 print_callstats(
216                                 "Server nfs v3:\n",
217                                 nfsv3name, svcv3info + 1, 22
218                                 );
219                 }
220         }
221
222         if (opt_clt) {
223                 if (opt_prt & PRNT_NET) {
224                         print_numbers(
225                         "Client packet stats:\n"
226                         "packets    udp        tcp        tcpconn\n",
227                         cltnetinfo, 4
228                         );
229                 }
230                 if (opt_prt & PRNT_RPC) {
231                         print_numbers(
232                         "Client rpc stats:\n"
233                         "calls      retrans    authrefrsh\n",
234                         cltrpcinfo, 3
235                         );
236                 }
237                 if (opt_prt & PRNT_CALLS) {
238                         print_callstats(
239                         "Client nfs v2:\n",
240                         nfsv2name, cltv2info + 1, 18
241                         );
242                         if (cltv3info[0])
243                                 print_callstats(
244                                 "Client nfs v3:\n",
245                                 nfsv3name, cltv3info + 1, 22
246                                 );
247                 }
248         }
249
250         return 0;
251 }
252
253 static void
254 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
255 {
256         unsigned int    i;
257
258         fputs(hdr, stdout);
259         for (i = 0; i < nr; i++)
260                 printf("%s%-8d", i? "   " : "", info[i]);
261         printf("\n");
262 }
263
264 static void
265 print_callstats(const char *hdr, const char **names,
266                                  unsigned int *info, unsigned int nr)
267 {
268         unsigned int    total;
269         int             i, j;
270
271         fputs(hdr, stdout);
272         for (i = 0, total = 0; i < nr; i++)
273                 total += info[i];
274         if (!total)
275                 total = 1;
276         for (i = 0; i < nr; i += 6) {
277                 for (j = 0; j < 6 && i + j < nr; j++)
278                         printf("%-11s", names[i+j]);
279                 printf("\n");
280                 for (j = 0; j < 6 && i + j < nr; j++)
281                         printf("%-6d %2d%% ",
282                                 info[i+j], 100 * info[i+j] / total);
283                 printf("\n");
284         }
285         printf("\n");
286 }
287
288 static int
289 parse_statfile(const char *name, struct statinfo *statp)
290 {
291         char    buffer[4096], *next;
292         FILE    *fp;
293
294         /* Being unable to read e.g. the nfsd stats file shouldn't
295          * be a fatal error -- it usually means the module isn't loaded.
296          */
297         if ((fp = fopen(name, "r")) == NULL) {
298                 fprintf(stderr, "Warning: %s: %m\n", name);
299                 return 1;
300         }
301
302         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
303                 struct statinfo *ip;
304                 char            *sp, *line = buffer;
305                 int             i, cnt;
306
307                 if ((next = strchr(line, '\n')) != NULL)
308                         *next++ = '\0';
309                 if (!(sp = strtok(line, " \t")))
310                         continue;
311                 for (ip = statp; ip->tag; ip++) {
312                         if (!strcmp(sp, ip->tag))
313                                 break;
314                 }
315                 if (!ip->tag)
316                         continue;
317                 cnt = ip->nrvals;
318
319                 for (i = 0; i < cnt; i++) {
320                         if (!(sp = strtok(NULL, " \t")))
321                                 break;
322                         ip->valptr[i] = atoi(sp);
323                 }
324         }
325
326         fclose(fp);
327         return 1;
328 }