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 static void nfs2_procedures_submit (unsigned long long *val, char *inst)
210 {
211 char buf[BUFSIZE];
212 int retval = 0;
214 retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
215 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
216 "%llu:%llu:%llu", /* 18x %llu */
217 (unsigned int) curtime,
218 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
219 val[7], val[8], val[9], val[10], val[11], val[12],
220 val[13], val[14], val[15], val[16], val[17]);
223 if (retval >= BUFSIZE)
224 return;
225 else if (retval < 0)
226 {
227 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
228 return;
229 }
231 plugin_submit ("nfs2_procedures", inst, buf);
232 }
234 static void nfs3_procedures_submit (unsigned long long *val, char *inst)
235 {
236 char buf[BUFSIZE];
237 int retval = 0;
239 retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
240 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
241 "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
242 (unsigned int) curtime,
243 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
244 val[7], val[8], val[9], val[10], val[11], val[12],
245 val[13], val[14], val[15], val[16], val[17], val[18],
246 val[19], val[20], val[21]);
248 if (retval >= BUFSIZE)
249 return;
250 else if (retval < 0)
251 {
252 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
253 return;
254 }
256 plugin_submit("nfs3_procedures", inst, buf);
257 }
259 #if defined(KERNEL_LINUX)
260 static void nfs_read_stats_file (FILE *fh, char *inst)
261 {
262 char buffer[BUFSIZE];
264 char *fields[48];
265 int numfields = 0;
267 if (fh == NULL)
268 return;
270 while (fgets (buffer, BUFSIZE, fh) != NULL)
271 {
272 numfields = strsplit (buffer, fields, 48);
274 if (numfields < 2)
275 continue;
277 if (strncmp (fields[0], "proc2", 5) == 0)
278 {
279 int i;
280 unsigned long long *values;
282 if (numfields - 2 != nfs2_procedures_ds_num)
283 {
284 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
285 continue;
286 }
288 if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
289 {
290 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
291 continue;
292 }
294 for (i = 0; i < nfs2_procedures_ds_num; i++)
295 values[i] = atoll (fields[i + 2]);
297 nfs2_procedures_submit (values, inst);
299 free (values);
300 }
301 else if (strncmp (fields[0], "proc3", 5) == 0)
302 {
303 int i;
304 unsigned long long *values;
306 if (numfields - 2 != nfs3_procedures_ds_num)
307 {
308 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
309 continue;
310 }
312 if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
313 {
314 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
315 continue;
316 }
318 for (i = 0; i < nfs3_procedures_ds_num; i++)
319 values[i] = atoll (fields[i + 2]);
321 nfs3_procedures_submit (values, inst);
323 free (values);
324 }
325 }
326 }
327 #endif /* defined(KERNEL_LINUX) */
328 #undef BUFSIZE
330 #ifdef HAVE_LIBKSTAT
331 static void nfs2_read_kstat (kstat_t *ksp, char *inst)
332 {
333 unsigned long long values[18];
335 values[0] = get_kstat_value (ksp, "null");
336 values[1] = get_kstat_value (ksp, "getattr");
337 values[2] = get_kstat_value (ksp, "setattr");
338 values[3] = get_kstat_value (ksp, "root");
339 values[4] = get_kstat_value (ksp, "lookup");
340 values[5] = get_kstat_value (ksp, "readlink");
341 values[6] = get_kstat_value (ksp, "read");
342 values[7] = get_kstat_value (ksp, "wrcache");
343 values[8] = get_kstat_value (ksp, "write");
344 values[9] = get_kstat_value (ksp, "create");
345 values[10] = get_kstat_value (ksp, "remove");
346 values[11] = get_kstat_value (ksp, "rename");
347 values[12] = get_kstat_value (ksp, "link");
348 values[13] = get_kstat_value (ksp, "symlink");
349 values[14] = get_kstat_value (ksp, "mkdir");
350 values[15] = get_kstat_value (ksp, "rmdir");
351 values[16] = get_kstat_value (ksp, "readdir");
352 values[17] = get_kstat_value (ksp, "statfs");
354 nfs2_procedures_submit (values, inst);
355 }
356 #endif
358 #if NFS_HAVE_READ
359 static void nfs_read (void)
360 {
361 #if defined(KERNEL_LINUX)
362 FILE *fh;
364 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
365 {
366 nfs_read_stats_file (fh, "client");
367 fclose (fh);
368 }
370 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
371 {
372 nfs_read_stats_file (fh, "server");
373 fclose (fh);
374 }
376 /* #endif defined(KERNEL_LINUX) */
378 #elif defined(HAVE_LIBKSTAT)
379 if (nfs2_ksp_client != NULL)
380 nfs2_read_kstat (nfs2_ksp_client, "client");
381 if (nfs2_ksp_server != NULL)
382 nfs2_read_kstat (nfs2_ksp_server, "server");
383 #endif /* defined(HAVE_LIBKSTAT) */
384 }
385 #else
386 # define nfs_read NULL
387 #endif /* NFS_HAVE_READ */
389 void module_register (void)
390 {
391 plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
392 plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
393 plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
394 }
396 #undef MODULE_NAME