]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsstat/nfsstat.c
Avoid overflow in nfsstats printing
[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                         opt_prt = 0;
128         int             c;
129
130         while ((c = getopt(argc, argv, "acno:rsz")) != -1) {
131                 switch (c) {
132                 case 'a':
133                         opt_all = 1;
134                         break;
135                 case 'c':
136                         opt_clt = 1;
137                         break;
138                 case 'n':
139                         opt_prt |= PRNT_CALLS;
140                         break;
141                 case 'o':
142                         if (!strcmp(optarg, "nfs"))
143                                 opt_prt |= PRNT_CALLS;
144                         else if (!strcmp(optarg, "rpc"))
145                                 opt_prt |= PRNT_RPC;
146                         else if (!strcmp(optarg, "net"))
147                                 opt_prt |= PRNT_NET;
148                         else if (!strcmp(optarg, "rc"))
149                                 opt_prt |= PRNT_RC;
150                         else if (!strcmp(optarg, "fh"))
151                                 opt_prt |= PRNT_FH;
152                         else {
153                                 fprintf(stderr, "nfsstat: unknown category: "
154                                                 "%s\n", optarg);
155                                 return 2;
156                         }
157                         break;
158                 case 'r':
159                         opt_prt |= PRNT_RPC;
160                         break;
161                 case 's':
162                         opt_srv = 1;
163                         break;
164                 case 'z':
165                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
166                                         "not yet supported\n");
167                         return 2;
168                 }
169         }
170
171         if (opt_all) {
172                 opt_srv = opt_clt = 1;
173                 opt_prt = PRNT_ALL;
174         }
175         if (!(opt_srv + opt_clt))
176                 opt_srv = opt_clt = 1;
177         if (!opt_prt)
178                 opt_prt = PRNT_CALLS + PRNT_RPC;
179         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
180                 fprintf(stderr,
181                         "You requested file handle or request cache "
182                         "statistics while using the -c option.\n"
183                         "This information is available only for the NFS "
184                         "server.\n");
185         }
186
187         if ((opt_srv && !parse_statfile(NFSSVCSTAT, svcinfo))
188          || (opt_clt && !parse_statfile(NFSCLTSTAT, cltinfo)))
189                 return 2;
190
191         if (opt_srv) {
192                 if (opt_prt & PRNT_NET) {
193                         print_numbers(
194                         "Server packet stats:\n"
195                         "packets    udp        tcp        tcpconn\n",
196                         svcnetinfo, 4
197                         );
198                 }
199                 if (opt_prt & PRNT_RPC) {
200                         print_numbers(
201                         "Server rpc stats:\n"
202                         "calls      badcalls   badauth    badclnt    xdrcall\n",
203                         svcrpcinfo, 5
204                         );
205                 }
206                 if (opt_prt & PRNT_RC) {
207                         print_numbers(
208                         "Server reply cache:\n"
209                         "hits       misses     nocache\n",
210                         svcrcinfo, 3
211                         );
212                 }
213
214                 /*
215                  * 2.2 puts all fh-related info after the 'rc' header
216                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
217                  *     'stale' to the start and swaps dir and nondir :-(  
218                  *     We preseve the 2.2 order
219                  */
220                 if (opt_prt & PRNT_FH) {
221                         if (get_stat_info("fh", svcinfo)) {     /* >= 2.4 */
222                                 int t = svcfhinfo[3];
223                                 svcfhinfo[3]=svcfhinfo[4];
224                                 svcfhinfo[4]=t;
225                                 
226                                 svcfhinfo[5]=svcfhinfo[0]; /* relocate 'stale' */
227                                 
228                                 print_numbers(
229                                         "Server file handle cache:\n"
230                                         "lookup     anon       ncachedir ncachedir  stale\n",
231                                         svcfhinfo + 1, 5);
232                         } else                                  /* < 2.4 */
233                                 print_numbers(
234                                         "Server file handle cache:\n"
235                                         "lookup     anon       ncachedir ncachedir  stale\n",
236                                         svcrcinfo + 3, 5);
237                 }
238                 if (opt_prt & PRNT_CALLS) {
239                         print_callstats(
240                         "Server nfs v2:\n",
241                         nfsv2name, svcv2info + 1, 18
242                         );
243                         if (svcv3info[0])
244                                 print_callstats(
245                                 "Server nfs v3:\n",
246                                 nfsv3name, svcv3info + 1, 22
247                                 );
248                 }
249         }
250
251         if (opt_clt) {
252                 if (opt_prt & PRNT_NET) {
253                         print_numbers(
254                         "Client packet stats:\n"
255                         "packets    udp        tcp        tcpconn\n",
256                         cltnetinfo, 4
257                         );
258                 }
259                 if (opt_prt & PRNT_RPC) {
260                         print_numbers(
261                         "Client rpc stats:\n"
262                         "calls      retrans    authrefrsh\n",
263                         cltrpcinfo, 3
264                         );
265                 }
266                 if (opt_prt & PRNT_CALLS) {
267                         print_callstats(
268                         "Client nfs v2:\n",
269                         nfsv2name, cltv2info + 1, 18
270                         );
271                         if (cltv3info[0])
272                                 print_callstats(
273                                 "Client nfs v3:\n",
274                                 nfsv3name, cltv3info + 1, 22
275                                 );
276                 }
277         }
278
279         return 0;
280 }
281
282 static statinfo *
283 get_stat_info(const char *sp, struct statinfo *statp)
284 {
285         struct statinfo *ip;
286
287         for (ip = statp; ip->tag; ip++) {
288                 if (!strcmp(sp, ip->tag))
289                         return ip;
290         }
291
292         return NULL;
293 }
294
295 static void
296 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
297 {
298         unsigned int    i;
299
300         fputs(hdr, stdout);
301         for (i = 0; i < nr; i++)
302                 printf("%s%-8d", i? "   " : "", info[i]);
303         printf("\n");
304 }
305
306 static void
307 print_callstats(const char *hdr, const char **names,
308                                  unsigned int *info, unsigned int nr)
309 {
310         unsigned long long      total;
311         unsigned long long      pct;
312         int             i, j;
313
314         fputs(hdr, stdout);
315         for (i = 0, total = 0; i < nr; i++)
316                 total += info[i];
317         if (!total)
318                 total = 1;
319         for (i = 0; i < nr; i += 6) {
320                 for (j = 0; j < 6 && i + j < nr; j++)
321                         printf("%-11s", names[i+j]);
322                 printf("\n");
323                 for (j = 0; j < 6 && i + j < nr; j++) {
324                         pct = ((unsigned long long) info[i+j]*100)/total;
325                         printf("%-6d %2llu%% ", info[i+j], pct);
326                 }
327                 printf("\n");
328         }
329         printf("\n");
330 }
331
332
333 static int
334 parse_statfile(const char *name, struct statinfo *statp)
335 {
336         char    buffer[4096], *next;
337         FILE    *fp;
338
339         /* Being unable to read e.g. the nfsd stats file shouldn't
340          * be a fatal error -- it usually means the module isn't loaded.
341          */
342         if ((fp = fopen(name, "r")) == NULL) {
343                 fprintf(stderr, "Warning: %s: %m\n", name);
344                 return 1;
345         }
346
347         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
348                 struct statinfo *ip;
349                 char            *sp, *line = buffer;
350                 int             i, cnt;
351
352                 if ((next = strchr(line, '\n')) != NULL)
353                         *next++ = '\0';
354                 if (!(sp = strtok(line, " \t")))
355                         continue;
356
357                 ip = get_stat_info(sp, statp);
358                 if (!ip)
359                         continue;
360
361                 cnt = ip->nrvals;
362
363                 for (i = 0; i < cnt; i++) {
364                         if (!(sp = strtok(NULL, " \t")))
365                                 break;
366                         ip->valptr[i] = atoi(sp);
367                 }
368         }
369
370         fclose(fp);
371         return 1;
372 }