1 /**
2 * collectd - src/nfs.c
3 * Copyright (C) 2005 Jason Pepas
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Jason Pepas <cell at ices.utexas.edu>
21 * Florian octo Forster <octo at verplant.org>
22 **/
24 #include "nfs.h"
26 #if COLLECT_NFS
27 #define MODULE_NAME "nfs"
29 #include "plugin.h"
30 #include "common.h"
32 static char *nfs2_procedures_file = "nfs2_procedures-%s.rrd";
33 static char *nfs3_procedures_file = "nfs3_procedures-%s.rrd";
35 /*
36 see /proc/net/rpc/nfs
37 see http://www.missioncriticallinux.com/orph/NFS-Statistics
39 net x x x x
40 rpc_stat.netcnt Not used; always zero.
41 rpc_stat.netudpcnt Not used; always zero.
42 rpc_stat.nettcpcnt Not used; always zero.
43 rpc_stat.nettcpconn Not used; always zero.
45 rpc x x x
46 rpc_stat.rpccnt The number of RPC calls.
47 rpc_stat.rpcretrans The number of retransmitted RPC calls.
48 rpc_stat.rpcauthrefresh The number of credential refreshes.
50 proc2 x x x...
51 proc3 x x x...
53 Procedure NFS Version NFS Version 3
54 Number Procedures Procedures
56 0 null null
57 1 getattr getattr
58 2 setattr setattr
59 3 root lookup
60 4 lookup access
61 5 readlink readlink
62 6 read read
63 7 wrcache write
64 8 write create
65 9 create mkdir
66 10 remove symlink
67 11 rename mknod
68 12 link remove
69 13 symlink rmdir
70 14 mkdir rename
71 15 rmdir link
72 16 readdir readdir
73 17 fsstat readdirplus
74 18 fsstat
75 19 fsinfo
76 20 pathconf
77 21 commit
78 */
80 static char *nfs2_procedures_ds_def[] =
81 {
82 "DS:null:COUNTER:25:0:U",
83 "DS:getattr:COUNTER:25:0:U",
84 "DS:setattr:COUNTER:25:0:U",
85 "DS:root:COUNTER:25:0:U",
86 "DS:lookup:COUNTER:25:0:U",
87 "DS:readlink:COUNTER:25:0:U",
88 "DS:read:COUNTER:25:0:U",
89 "DS:wrcache:COUNTER:25:0:U",
90 "DS:write:COUNTER:25:0:U",
91 "DS:create:COUNTER:25:0:U",
92 "DS:remove:COUNTER:25:0:U",
93 "DS:rename:COUNTER:25:0:U",
94 "DS:link:COUNTER:25:0:U",
95 "DS:symlink:COUNTER:25:0:U",
96 "DS:mkdir:COUNTER:25:0:U",
97 "DS:rmdir:COUNTER:25:0:U",
98 "DS:readdir:COUNTER:25:0:U",
99 "DS:fsstat:COUNTER:25:0:U",
100 NULL
101 };
102 static int nfs2_procedures_ds_num = 18;
104 static char *nfs3_procedures_ds_def[] =
105 {
106 "DS:null:COUNTER:25:0:U",
107 "DS:getattr:COUNTER:25:0:U",
108 "DS:setattr:COUNTER:25:0:U",
109 "DS:lookup:COUNTER:25:0:U",
110 "DS:access:COUNTER:25:0:U",
111 "DS:readlink:COUNTER:25:0:U",
112 "DS:read:COUNTER:25:0:U",
113 "DS:write:COUNTER:25:0:U",
114 "DS:create:COUNTER:25:0:U",
115 "DS:mkdir:COUNTER:25:0:U",
116 "DS:symlink:COUNTER:25:0:U",
117 "DS:mknod:COUNTER:25:0:U",
118 "DS:remove:COUNTER:25:0:U",
119 "DS:rmdir:COUNTER:25:0:U",
120 "DS:rename:COUNTER:25:0:U",
121 "DS:link:COUNTER:25:0:U",
122 "DS:readdir:COUNTER:25:0:U",
123 "DS:readdirplus:COUNTER:25:0:U",
124 "DS:fsstat:COUNTER:25:0:U",
125 "DS:fsinfo:COUNTER:25:0:U",
126 "DS:pathconf:COUNTER:25:0:U",
127 "DS:commit:COUNTER:25:0:U",
128 NULL
129 };
130 static int nfs3_procedures_ds_num = 22;
132 #ifdef HAVE_LIBKSTAT
133 extern kstat_ctl_t *kc;
134 static kstat_t *nfs2_ksp_client;
135 static kstat_t *nfs2_ksp_server;
136 static kstat_t *nfs3_ksp_client;
137 static kstat_t *nfs3_ksp_server;
138 static kstat_t *nfs4_ksp_client;
139 static kstat_t *nfs4_ksp_server;
140 #endif
142 /* Possibly TODO: NFSv4 statistics */
144 extern time_t curtime;
146 void nfs_init (void)
147 {
148 #ifdef HAVE_LIBKSTAT
149 kstat_t *ksp_chain;
151 nfs2_ksp_client = NULL;
152 nfs2_ksp_server = NULL;
153 nfs3_ksp_client = NULL;
154 nfs3_ksp_server = NULL;
155 nfs4_ksp_client = NULL;
156 nfs4_ksp_server = NULL;
158 if (kc == NULL)
159 return;
161 for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
162 ksp_chain = ksp_chain->ks_next)
163 {
164 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
165 continue;
166 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
167 nfs2_ksp_server = ksp_chain;
168 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
169 nfs3_ksp_server = ksp_chain;
170 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
171 nfs4_ksp_server = ksp_chain;
172 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
173 nfs2_ksp_client = ksp_chain;
174 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
175 nfs3_ksp_client = ksp_chain;
176 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
177 nfs4_ksp_client = ksp_chain;
178 }
179 #endif
181 return;
182 }
184 #define BUFSIZE 1024
185 void nfs2_procedures_write (char *host, char *inst, char *val)
186 {
187 char filename[BUFSIZE];
189 if (snprintf (filename, BUFSIZE, nfs2_procedures_file, inst) > BUFSIZE)
190 return;
192 rrd_update_file (host, filename, val, nfs2_procedures_ds_def,
193 nfs2_procedures_ds_num);
194 }
196 void nfs3_procedures_write (char *host, char *inst, char *val)
197 {
198 char filename[BUFSIZE];
200 if (snprintf (filename, BUFSIZE, nfs3_procedures_file, inst) > BUFSIZE)
201 return;
203 rrd_update_file (host, filename, val, nfs3_procedures_ds_def,
204 nfs3_procedures_ds_num);
205 }
207 void nfs2_procedures_submit (unsigned long long *val, char *inst)
208 {
209 char buf[BUFSIZE];
210 int retval = 0;
212 retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
213 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
214 "%llu:%llu:%llu", /* 18x %llu */
215 (unsigned int) curtime,
216 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
217 val[7], val[8], val[9], val[10], val[11], val[12],
218 val[13], val[14], val[15], val[16], val[17]);
221 if (retval >= BUFSIZE)
222 return;
223 else if (retval < 0)
224 {
225 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
226 return;
227 }
229 plugin_submit ("nfs2_procedures", inst, buf);
230 }
232 void nfs3_procedures_submit (unsigned long long *val, char *inst)
233 {
234 char buf[BUFSIZE];
235 int retval = 0;
237 retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
238 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
239 "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
240 (unsigned int) curtime,
241 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
242 val[7], val[8], val[9], val[10], val[11], val[12],
243 val[13], val[14], val[15], val[16], val[17], val[18],
244 val[19], val[20], val[21]);
246 if (retval >= BUFSIZE)
247 return;
248 else if (retval < 0)
249 {
250 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
251 return;
252 }
254 plugin_submit("nfs3_procedures", inst, buf);
255 }
257 #if defined(KERNEL_LINUX)
258 void nfs_read_stats_file (FILE *fh, char *inst)
259 {
260 char buffer[BUFSIZE];
262 char *fields[48];
263 int numfields = 0;
265 if (fh == NULL)
266 return;
268 while (fgets (buffer, BUFSIZE, fh) != NULL)
269 {
270 numfields = strsplit (buffer, fields, 48);
272 if (numfields < 2)
273 continue;
275 if (strncmp (fields[0], "proc2", 5) == 0)
276 {
277 int i;
278 unsigned long long *values;
280 if (numfields - 2 != nfs2_procedures_ds_num)
281 {
282 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
283 continue;
284 }
286 if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
287 {
288 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
289 continue;
290 }
292 for (i = 0; i < nfs2_procedures_ds_num; i++)
293 values[i] = atoll (fields[i + 2]);
295 nfs2_procedures_submit (values, inst);
297 free (values);
298 }
299 else if (strncmp (fields[0], "proc3", 5) == 0)
300 {
301 int i;
302 unsigned long long *values;
304 if (numfields - 2 != nfs3_procedures_ds_num)
305 {
306 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
307 continue;
308 }
310 if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
311 {
312 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
313 continue;
314 }
316 for (i = 0; i < nfs3_procedures_ds_num; i++)
317 values[i] = atoll (fields[i + 2]);
319 nfs3_procedures_submit (values, inst);
321 free (values);
322 }
323 }
324 }
325 #endif /* defined(KERNEL_LINUX) */
326 #undef BUFSIZE
328 #ifdef HAVE_LIBKSTAT
329 void nfs2_read_kstat (kstat_t *ksp, char *inst)
330 {
331 unsigned long long values[18];
333 values[0] = get_kstat_value (ksp, "null");
334 values[1] = get_kstat_value (ksp, "getattr");
335 values[2] = get_kstat_value (ksp, "setattr");
336 values[3] = get_kstat_value (ksp, "root");
337 values[4] = get_kstat_value (ksp, "lookup");
338 values[5] = get_kstat_value (ksp, "readlink");
339 values[6] = get_kstat_value (ksp, "read");
340 values[7] = get_kstat_value (ksp, "wrcache");
341 values[8] = get_kstat_value (ksp, "write");
342 values[9] = get_kstat_value (ksp, "create");
343 values[10] = get_kstat_value (ksp, "remove");
344 values[11] = get_kstat_value (ksp, "rename");
345 values[12] = get_kstat_value (ksp, "link");
346 values[13] = get_kstat_value (ksp, "symlink");
347 values[14] = get_kstat_value (ksp, "mkdir");
348 values[15] = get_kstat_value (ksp, "rmdir");
349 values[16] = get_kstat_value (ksp, "readdir");
350 values[17] = get_kstat_value (ksp, "statfs");
352 nfs2_procedures_submit (values, inst);
353 }
354 #endif
356 void nfs_read (void)
357 {
358 #if defined(KERNEL_LINUX)
359 FILE *fh;
361 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
362 {
363 nfs_read_stats_file (fh, "client");
364 fclose (fh);
365 }
367 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
368 {
369 nfs_read_stats_file (fh, "server");
370 fclose (fh);
371 }
373 /* #endif defined(KERNEL_LINUX) */
375 #elif defined(HAVE_LIBKSTAT)
376 if (nfs2_ksp_client != NULL)
377 nfs2_read_kstat (nfs2_ksp_client, "client");
378 if (nfs2_ksp_server != NULL)
379 nfs2_read_kstat (nfs2_ksp_server, "server");
380 #endif /* defined(HAVE_LIBKSTAT) */
381 }
383 void module_register (void)
384 {
385 plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
386 plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
387 plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
388 }
390 #undef MODULE_NAME
391 #endif /* COLLECT_LOAD */