1 /**
2 * collectd - src/nfs.c
3 * Copyright (C) 2005,2006 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; only version 2 of the License is applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Jason Pepas <cell at ices.utexas.edu>
20 * Florian octo Forster <octo at verplant.org>
21 **/
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
27 /* #if defined(KERNEL_LINUX) || defined(HAVE_LIBKSTAT) */
28 #if KERNEL_LINUX
29 # define NFS_HAVE_READ 1
30 #else
31 # define NFS_HAVE_READ 0
32 #endif
34 /*
35 see /proc/net/rpc/nfs
36 see http://www.missioncriticallinux.com/orph/NFS-Statistics
38 net x x x x
39 rpc_stat.netcnt Not used; always zero.
40 rpc_stat.netudpcnt Not used; always zero.
41 rpc_stat.nettcpcnt Not used; always zero.
42 rpc_stat.nettcpconn Not used; always zero.
44 rpc x x x
45 rpc_stat.rpccnt The number of RPC calls.
46 rpc_stat.rpcretrans The number of retransmitted RPC calls.
47 rpc_stat.rpcauthrefresh The number of credential refreshes.
49 proc2 x x x...
50 proc3 x x x...
52 Procedure NFS Version NFS Version 3
53 Number Procedures Procedures
55 0 null null
56 1 getattr getattr
57 2 setattr setattr
58 3 root lookup
59 4 lookup access
60 5 readlink readlink
61 6 read read
62 7 wrcache write
63 8 write create
64 9 create mkdir
65 10 remove symlink
66 11 rename mknod
67 12 link remove
68 13 symlink rmdir
69 14 mkdir rename
70 15 rmdir link
71 16 readdir readdir
72 17 fsstat readdirplus
73 18 fsstat
74 19 fsinfo
75 20 pathconf
76 21 commit
77 */
79 static data_source_t procedure_dsrc[1] =
80 {
81 {"value", DS_TYPE_COUNTER, 0, 4294967295.0}
82 };
84 static data_set_t procedure_ds =
85 {
86 "nfs_procedure", 1, procedure_dsrc
87 };
89 #if NFS_HAVE_READ
90 static const char *nfs2_procedures_names[] =
91 {
92 "null",
93 "getattr",
94 "setattr",
95 "root",
96 "lookup",
97 "readlink",
98 "read",
99 "wrcache",
100 "write",
101 "create",
102 "remove",
103 "rename",
104 "link",
105 "symlink",
106 "mkdir",
107 "rmdir",
108 "readdir",
109 "fsstat",
110 NULL
111 };
112 static int nfs2_procedures_names_num = 18;
114 static const char *nfs3_procedures_names[] =
115 {
116 "null",
117 "getattr",
118 "setattr",
119 "lookup",
120 "access",
121 "readlink",
122 "read",
123 "write",
124 "create",
125 "mkdir",
126 "symlink",
127 "mknod",
128 "remove",
129 "rmdir",
130 "rename",
131 "link",
132 "readdir",
133 "readdirplus",
134 "fsstat",
135 "fsinfo",
136 "pathconf",
137 "commit",
138 NULL
139 };
140 static int nfs3_procedures_names_num = 22;
142 #if HAVE_LIBKSTAT && 0
143 extern kstat_ctl_t *kc;
144 static kstat_t *nfs2_ksp_client;
145 static kstat_t *nfs2_ksp_server;
146 static kstat_t *nfs3_ksp_client;
147 static kstat_t *nfs3_ksp_server;
148 static kstat_t *nfs4_ksp_client;
149 static kstat_t *nfs4_ksp_server;
150 #endif
152 /* Possibly TODO: NFSv4 statistics */
154 #if 0
155 static int nfs_init (void)
156 {
157 #if HAVE_LIBKSTAT && 0
158 kstat_t *ksp_chain;
160 nfs2_ksp_client = NULL;
161 nfs2_ksp_server = NULL;
162 nfs3_ksp_client = NULL;
163 nfs3_ksp_server = NULL;
164 nfs4_ksp_client = NULL;
165 nfs4_ksp_server = NULL;
167 if (kc == NULL)
168 return;
170 for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
171 ksp_chain = ksp_chain->ks_next)
172 {
173 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
174 continue;
175 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
176 nfs2_ksp_server = ksp_chain;
177 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
178 nfs3_ksp_server = ksp_chain;
179 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
180 nfs4_ksp_server = ksp_chain;
181 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
182 nfs2_ksp_client = ksp_chain;
183 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
184 nfs3_ksp_client = ksp_chain;
185 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
186 nfs4_ksp_client = ksp_chain;
187 }
188 #endif
190 return (0);
191 } /* int nfs_init */
192 #endif
194 #define BUFSIZE 1024
195 static void nfs_procedures_submit (const char *plugin_instance,
196 unsigned long long *val, const char **names, int len)
197 {
198 value_t values[1];
199 value_list_t vl = VALUE_LIST_INIT;
200 int i;
202 vl.values = values;
203 vl.values_len = 1;
204 vl.time = time (NULL);
205 strcpy (vl.host, hostname_g);
206 strcpy (vl.plugin, "nfs");
207 strncpy (vl.plugin_instance, plugin_instance,
208 sizeof (vl.plugin_instance));
210 for (i = 0; i < len; i++)
211 {
212 values[0].counter = val[i];
213 strncpy (vl.type_instance, names[i],
214 sizeof (vl.type_instance));
215 DEBUG ("%s-%s/nfs_procedure-%s = %llu",
216 vl.plugin, vl.plugin_instance,
217 vl.type_instance, val[i]);
218 plugin_dispatch_values ("nfs_procedure", &vl);
219 }
220 } /* void nfs_procedures_submit */
222 #if KERNEL_LINUX
223 static void nfs_read_stats_file (FILE *fh, char *inst)
224 {
225 char buffer[BUFSIZE];
227 char plugin_instance[DATA_MAX_NAME_LEN];
229 char *fields[48];
230 int numfields = 0;
232 if (fh == NULL)
233 return;
235 while (fgets (buffer, BUFSIZE, fh) != NULL)
236 {
237 numfields = strsplit (buffer, fields, 48);
239 if (((numfields - 2) != nfs2_procedures_names_num)
240 && ((numfields - 2)
241 != nfs3_procedures_names_num))
242 continue;
244 if (strcmp (fields[0], "proc2") == 0)
245 {
246 int i;
247 unsigned long long *values;
249 if ((numfields - 2) != nfs2_procedures_names_num)
250 {
251 WARNING ("nfs plugin: Wrong "
252 "number of fields (= %i) "
253 "for NFSv2 statistics.",
254 numfields - 2);
255 continue;
256 }
258 snprintf (plugin_instance, sizeof (plugin_instance),
259 "v2%s", inst);
260 plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
262 values = (unsigned long long *) malloc (nfs2_procedures_names_num * sizeof (unsigned long long));
263 if (values == NULL)
264 {
265 char errbuf[1024];
266 ERROR ("nfs plugin: malloc "
267 "failed: %s",
268 sstrerror (errno, errbuf, sizeof (errbuf)));
269 continue;
270 }
272 for (i = 0; i < nfs2_procedures_names_num; i++)
273 values[i] = atoll (fields[i + 2]);
275 nfs_procedures_submit (plugin_instance, values,
276 nfs2_procedures_names,
277 nfs2_procedures_names_num);
279 free (values);
280 }
281 else if (strncmp (fields[0], "proc3", 5) == 0)
282 {
283 int i;
284 unsigned long long *values;
286 if ((numfields - 2) != nfs3_procedures_names_num)
287 {
288 WARNING ("nfs plugin: Wrong "
289 "number of fields (= %i) "
290 "for NFSv3 statistics.",
291 numfields - 2);
292 continue;
293 }
295 snprintf (plugin_instance, sizeof (plugin_instance),
296 "v3%s", inst);
297 plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
299 values = (unsigned long long *) malloc (nfs3_procedures_names_num * sizeof (unsigned long long));
300 if (values == NULL)
301 {
302 char errbuf[1024];
303 ERROR ("nfs plugin: malloc "
304 "failed: %s",
305 sstrerror (errno, errbuf, sizeof (errbuf)));
306 continue;
307 }
309 for (i = 0; i < nfs3_procedures_names_num; i++)
310 values[i] = atoll (fields[i + 2]);
312 nfs_procedures_submit (plugin_instance, values,
313 nfs3_procedures_names,
314 nfs3_procedures_names_num);
316 free (values);
317 }
318 } /* while (fgets (buffer, BUFSIZE, fh) != NULL) */
319 } /* void nfs_read_stats_file */
320 #endif /* defined(KERNEL_LINUX) */
321 #undef BUFSIZE
323 #if HAVE_LIBKSTAT && 0
324 static void nfs2_read_kstat (kstat_t *ksp, char *inst)
325 {
326 unsigned long long values[18];
328 values[0] = get_kstat_value (ksp, "null");
329 values[1] = get_kstat_value (ksp, "getattr");
330 values[2] = get_kstat_value (ksp, "setattr");
331 values[3] = get_kstat_value (ksp, "root");
332 values[4] = get_kstat_value (ksp, "lookup");
333 values[5] = get_kstat_value (ksp, "readlink");
334 values[6] = get_kstat_value (ksp, "read");
335 values[7] = get_kstat_value (ksp, "wrcache");
336 values[8] = get_kstat_value (ksp, "write");
337 values[9] = get_kstat_value (ksp, "create");
338 values[10] = get_kstat_value (ksp, "remove");
339 values[11] = get_kstat_value (ksp, "rename");
340 values[12] = get_kstat_value (ksp, "link");
341 values[13] = get_kstat_value (ksp, "symlink");
342 values[14] = get_kstat_value (ksp, "mkdir");
343 values[15] = get_kstat_value (ksp, "rmdir");
344 values[16] = get_kstat_value (ksp, "readdir");
345 values[17] = get_kstat_value (ksp, "statfs");
347 nfs2_procedures_submit (values, inst);
348 }
349 #endif
351 static int nfs_read (void)
352 {
353 #if KERNEL_LINUX
354 FILE *fh;
356 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
357 {
358 nfs_read_stats_file (fh, "client");
359 fclose (fh);
360 }
362 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
363 {
364 nfs_read_stats_file (fh, "server");
365 fclose (fh);
366 }
368 /* #endif defined(KERNEL_LINUX) */
370 #elif HAVE_LIBKSTAT && 0
371 if (nfs2_ksp_client != NULL)
372 nfs2_read_kstat (nfs2_ksp_client, "client");
373 if (nfs2_ksp_server != NULL)
374 nfs2_read_kstat (nfs2_ksp_server, "server");
375 #endif /* defined(HAVE_LIBKSTAT) */
377 return (0);
378 }
379 #endif /* NFS_HAVE_READ */
381 void module_register (modreg_e load)
382 {
383 if (load & MR_DATASETS)
384 plugin_register_data_set (&procedure_ds);
386 #if NFS_HAVE_READ
387 if (load & MR_READ)
388 plugin_register_read ("nfs", nfs_read);
389 #endif
390 } /* void module_register */