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 "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
28 #define MODULE_NAME "nfs"
30 #if defined(KERNEL_LINUX) || defined(HAVE_LIBKSTAT)
31 # define NFS_HAVE_READ 1
32 #else
33 # define NFS_HAVE_READ 0
34 #endif
36 static char *nfs2_procedures_file = "nfs2_procedures-%s.rrd";
37 static char *nfs3_procedures_file = "nfs3_procedures-%s.rrd";
39 /*
40 see /proc/net/rpc/nfs
41 see http://www.missioncriticallinux.com/orph/NFS-Statistics
43 net x x x x
44 rpc_stat.netcnt Not used; always zero.
45 rpc_stat.netudpcnt Not used; always zero.
46 rpc_stat.nettcpcnt Not used; always zero.
47 rpc_stat.nettcpconn Not used; always zero.
49 rpc x x x
50 rpc_stat.rpccnt The number of RPC calls.
51 rpc_stat.rpcretrans The number of retransmitted RPC calls.
52 rpc_stat.rpcauthrefresh The number of credential refreshes.
54 proc2 x x x...
55 proc3 x x x...
57 Procedure NFS Version NFS Version 3
58 Number Procedures Procedures
60 0 null null
61 1 getattr getattr
62 2 setattr setattr
63 3 root lookup
64 4 lookup access
65 5 readlink readlink
66 6 read read
67 7 wrcache write
68 8 write create
69 9 create mkdir
70 10 remove symlink
71 11 rename mknod
72 12 link remove
73 13 symlink rmdir
74 14 mkdir rename
75 15 rmdir link
76 16 readdir readdir
77 17 fsstat readdirplus
78 18 fsstat
79 19 fsinfo
80 20 pathconf
81 21 commit
82 */
84 static char *nfs2_procedures_ds_def[] =
85 {
86 "DS:null:COUNTER:25:0:U",
87 "DS:getattr:COUNTER:25:0:U",
88 "DS:setattr:COUNTER:25:0:U",
89 "DS:root:COUNTER:25:0:U",
90 "DS:lookup:COUNTER:25:0:U",
91 "DS:readlink:COUNTER:25:0:U",
92 "DS:read:COUNTER:25:0:U",
93 "DS:wrcache:COUNTER:25:0:U",
94 "DS:write:COUNTER:25:0:U",
95 "DS:create:COUNTER:25:0:U",
96 "DS:remove:COUNTER:25:0:U",
97 "DS:rename:COUNTER:25:0:U",
98 "DS:link:COUNTER:25:0:U",
99 "DS:symlink:COUNTER:25:0:U",
100 "DS:mkdir:COUNTER:25:0:U",
101 "DS:rmdir:COUNTER:25:0:U",
102 "DS:readdir:COUNTER:25:0:U",
103 "DS:fsstat:COUNTER:25:0:U",
104 NULL
105 };
106 static int nfs2_procedures_ds_num = 18;
108 static char *nfs3_procedures_ds_def[] =
109 {
110 "DS:null:COUNTER:25:0:U",
111 "DS:getattr:COUNTER:25:0:U",
112 "DS:setattr:COUNTER:25:0:U",
113 "DS:lookup:COUNTER:25:0:U",
114 "DS:access:COUNTER:25:0:U",
115 "DS:readlink:COUNTER:25:0:U",
116 "DS:read:COUNTER:25:0:U",
117 "DS:write:COUNTER:25:0:U",
118 "DS:create:COUNTER:25:0:U",
119 "DS:mkdir:COUNTER:25:0:U",
120 "DS:symlink:COUNTER:25:0:U",
121 "DS:mknod:COUNTER:25:0:U",
122 "DS:remove:COUNTER:25:0:U",
123 "DS:rmdir:COUNTER:25:0:U",
124 "DS:rename:COUNTER:25:0:U",
125 "DS:link:COUNTER:25:0:U",
126 "DS:readdir:COUNTER:25:0:U",
127 "DS:readdirplus:COUNTER:25:0:U",
128 "DS:fsstat:COUNTER:25:0:U",
129 "DS:fsinfo:COUNTER:25:0:U",
130 "DS:pathconf:COUNTER:25:0:U",
131 "DS:commit:COUNTER:25:0:U",
132 NULL
133 };
134 static int nfs3_procedures_ds_num = 22;
136 #ifdef HAVE_LIBKSTAT
137 extern kstat_ctl_t *kc;
138 static kstat_t *nfs2_ksp_client;
139 static kstat_t *nfs2_ksp_server;
140 static kstat_t *nfs3_ksp_client;
141 static kstat_t *nfs3_ksp_server;
142 static kstat_t *nfs4_ksp_client;
143 static kstat_t *nfs4_ksp_server;
144 #endif
146 /* Possibly TODO: NFSv4 statistics */
148 static void nfs_init (void)
149 {
150 #ifdef HAVE_LIBKSTAT
151 kstat_t *ksp_chain;
153 nfs2_ksp_client = NULL;
154 nfs2_ksp_server = NULL;
155 nfs3_ksp_client = NULL;
156 nfs3_ksp_server = NULL;
157 nfs4_ksp_client = NULL;
158 nfs4_ksp_server = NULL;
160 if (kc == NULL)
161 return;
163 for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
164 ksp_chain = ksp_chain->ks_next)
165 {
166 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
167 continue;
168 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
169 nfs2_ksp_server = ksp_chain;
170 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
171 nfs3_ksp_server = ksp_chain;
172 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
173 nfs4_ksp_server = ksp_chain;
174 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
175 nfs2_ksp_client = ksp_chain;
176 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
177 nfs3_ksp_client = ksp_chain;
178 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
179 nfs4_ksp_client = ksp_chain;
180 }
181 #endif
183 return;
184 }
186 #define BUFSIZE 1024
187 static void nfs2_procedures_write (char *host, char *inst, char *val)
188 {
189 char filename[BUFSIZE];
191 if (snprintf (filename, BUFSIZE, nfs2_procedures_file, inst) > BUFSIZE)
192 return;
194 rrd_update_file (host, filename, val, nfs2_procedures_ds_def,
195 nfs2_procedures_ds_num);
196 }
198 static void nfs3_procedures_write (char *host, char *inst, char *val)
199 {
200 char filename[BUFSIZE];
202 if (snprintf (filename, BUFSIZE, nfs3_procedures_file, inst) > BUFSIZE)
203 return;
205 rrd_update_file (host, filename, val, nfs3_procedures_ds_def,
206 nfs3_procedures_ds_num);
207 }
209 #if NFS_HAVE_READ
210 static void nfs2_procedures_submit (unsigned long long *val, char *inst)
211 {
212 char buf[BUFSIZE];
213 int retval = 0;
215 retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
216 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
217 "%llu:%llu:%llu", /* 18x %llu */
218 (unsigned int) curtime,
219 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
220 val[7], val[8], val[9], val[10], val[11], val[12],
221 val[13], val[14], val[15], val[16], val[17]);
224 if (retval >= BUFSIZE)
225 return;
226 else if (retval < 0)
227 {
228 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
229 return;
230 }
232 plugin_submit ("nfs2_procedures", inst, buf);
233 }
235 static void nfs3_procedures_submit (unsigned long long *val, char *inst)
236 {
237 char buf[BUFSIZE];
238 int retval = 0;
240 retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
241 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
242 "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
243 (unsigned int) curtime,
244 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
245 val[7], val[8], val[9], val[10], val[11], val[12],
246 val[13], val[14], val[15], val[16], val[17], val[18],
247 val[19], val[20], val[21]);
249 if (retval >= BUFSIZE)
250 return;
251 else if (retval < 0)
252 {
253 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
254 return;
255 }
257 plugin_submit("nfs3_procedures", inst, buf);
258 }
259 #endif /* NFS_HAVE_READ */
261 #if defined(KERNEL_LINUX)
262 static void nfs_read_stats_file (FILE *fh, char *inst)
263 {
264 char buffer[BUFSIZE];
266 char *fields[48];
267 int numfields = 0;
269 if (fh == NULL)
270 return;
272 while (fgets (buffer, BUFSIZE, fh) != NULL)
273 {
274 numfields = strsplit (buffer, fields, 48);
276 if (numfields < 2)
277 continue;
279 if (strncmp (fields[0], "proc2", 5) == 0)
280 {
281 int i;
282 unsigned long long *values;
284 if (numfields - 2 != nfs2_procedures_ds_num)
285 {
286 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
287 continue;
288 }
290 if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
291 {
292 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
293 continue;
294 }
296 for (i = 0; i < nfs2_procedures_ds_num; i++)
297 values[i] = atoll (fields[i + 2]);
299 nfs2_procedures_submit (values, inst);
301 free (values);
302 }
303 else if (strncmp (fields[0], "proc3", 5) == 0)
304 {
305 int i;
306 unsigned long long *values;
308 if (numfields - 2 != nfs3_procedures_ds_num)
309 {
310 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
311 continue;
312 }
314 if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
315 {
316 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
317 continue;
318 }
320 for (i = 0; i < nfs3_procedures_ds_num; i++)
321 values[i] = atoll (fields[i + 2]);
323 nfs3_procedures_submit (values, inst);
325 free (values);
326 }
327 }
328 }
329 #endif /* defined(KERNEL_LINUX) */
330 #undef BUFSIZE
332 #ifdef HAVE_LIBKSTAT
333 static void nfs2_read_kstat (kstat_t *ksp, char *inst)
334 {
335 unsigned long long values[18];
337 values[0] = get_kstat_value (ksp, "null");
338 values[1] = get_kstat_value (ksp, "getattr");
339 values[2] = get_kstat_value (ksp, "setattr");
340 values[3] = get_kstat_value (ksp, "root");
341 values[4] = get_kstat_value (ksp, "lookup");
342 values[5] = get_kstat_value (ksp, "readlink");
343 values[6] = get_kstat_value (ksp, "read");
344 values[7] = get_kstat_value (ksp, "wrcache");
345 values[8] = get_kstat_value (ksp, "write");
346 values[9] = get_kstat_value (ksp, "create");
347 values[10] = get_kstat_value (ksp, "remove");
348 values[11] = get_kstat_value (ksp, "rename");
349 values[12] = get_kstat_value (ksp, "link");
350 values[13] = get_kstat_value (ksp, "symlink");
351 values[14] = get_kstat_value (ksp, "mkdir");
352 values[15] = get_kstat_value (ksp, "rmdir");
353 values[16] = get_kstat_value (ksp, "readdir");
354 values[17] = get_kstat_value (ksp, "statfs");
356 nfs2_procedures_submit (values, inst);
357 }
358 #endif
360 #if NFS_HAVE_READ
361 static void nfs_read (void)
362 {
363 #if defined(KERNEL_LINUX)
364 FILE *fh;
366 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
367 {
368 nfs_read_stats_file (fh, "client");
369 fclose (fh);
370 }
372 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
373 {
374 nfs_read_stats_file (fh, "server");
375 fclose (fh);
376 }
378 /* #endif defined(KERNEL_LINUX) */
380 #elif defined(HAVE_LIBKSTAT)
381 if (nfs2_ksp_client != NULL)
382 nfs2_read_kstat (nfs2_ksp_client, "client");
383 if (nfs2_ksp_server != NULL)
384 nfs2_read_kstat (nfs2_ksp_server, "server");
385 #endif /* defined(HAVE_LIBKSTAT) */
386 }
387 #else
388 # define nfs_read NULL
389 #endif /* NFS_HAVE_READ */
391 void module_register (void)
392 {
393 plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
394 plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
395 plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
396 }
398 #undef MODULE_NAME