1 /**
2 * collectd - src/nfs.c
3 * Copyright (C) 2005,2006 Jason Pepas
4 * Copyright (C) 2012,2013 Florian Forster
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; only version 2 of the License is applicable.
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 collectd.org>
22 * Cosmin Ioiart <cioiart at gmail.com>
23 **/
25 #include "collectd.h"
26 #include "common.h"
27 #include "plugin.h"
29 #if HAVE_KSTAT_H
30 #include <kstat.h>
31 #endif
33 /*
34 see /proc/net/rpc/nfs
35 see http://www.missioncriticallinux.com/orph/NFS-Statistics
37 net x x x x
38 rpc_stat.netcnt Not used; always zero.
39 rpc_stat.netudpcnt Not used; always zero.
40 rpc_stat.nettcpcnt Not used; always zero.
41 rpc_stat.nettcpconn Not used; always zero.
43 rpc x x x
44 rpc_stat.rpccnt The number of RPC calls.
45 rpc_stat.rpcretrans The number of retransmitted RPC calls.
46 rpc_stat.rpcauthrefresh The number of credential refreshes.
48 proc2 x x x...
49 proc3 x x x...
51 Procedure NFS Version NFS Version 3
52 Number Procedures Procedures
54 0 null null
55 1 getattr getattr
56 2 setattr setattr
57 3 root lookup
58 4 lookup access
59 5 readlink readlink
60 6 read read
61 7 wrcache write
62 8 write create
63 9 create mkdir
64 10 remove symlink
65 11 rename mknod
66 12 link remove
67 13 symlink rmdir
68 14 mkdir rename
69 15 rmdir link
70 16 readdir readdir
71 17 fsstat readdirplus
72 18 fsstat
73 19 fsinfo
74 20 pathconf
75 21 commit
76 */
78 static const char *nfs2_procedures_names[] =
79 {
80 "null",
81 "getattr",
82 "setattr",
83 "root",
84 "lookup",
85 "readlink",
86 "read",
87 "wrcache",
88 "write",
89 "create",
90 "remove",
91 "rename",
92 "link",
93 "symlink",
94 "mkdir",
95 "rmdir",
96 "readdir",
97 "fsstat"
98 };
99 static size_t nfs2_procedures_names_num = STATIC_ARRAY_SIZE (nfs2_procedures_names);
101 static const char *nfs3_procedures_names[] =
102 {
103 "null",
104 "getattr",
105 "setattr",
106 "lookup",
107 "access",
108 "readlink",
109 "read",
110 "write",
111 "create",
112 "mkdir",
113 "symlink",
114 "mknod",
115 "remove",
116 "rmdir",
117 "rename",
118 "link",
119 "readdir",
120 "readdirplus",
121 "fsstat",
122 "fsinfo",
123 "pathconf",
124 "commit"
125 };
126 static size_t nfs3_procedures_names_num = STATIC_ARRAY_SIZE (nfs3_procedures_names);
128 #if HAVE_LIBKSTAT
129 static const char *nfs4_procedures_names[] =
130 {
131 "null",
132 "compound",
133 "reserved",
134 "access",
135 "close",
136 "commit",
137 "create",
138 "delegpurge",
139 "delegreturn",
140 "getattr",
141 "getfh",
142 "link",
143 "lock",
144 "lockt",
145 "locku",
146 "lookup",
147 "lookupp",
148 "nverify",
149 "open",
150 "openattr",
151 "open_confirm",
152 "open_downgrade",
153 "putfh",
154 "putpubfh",
155 "putrootfh",
156 "read",
157 "readdir",
158 "readlink",
159 "remove",
160 "rename",
161 "renew",
162 "restorefh",
163 "savefh",
164 "secinfo",
165 "setattr",
166 "setclientid",
167 "setclientid_confirm",
168 "verify",
169 "write"
170 };
171 static size_t nfs4_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_procedures_names);
172 #endif
174 #if HAVE_LIBKSTAT
175 extern kstat_ctl_t *kc;
176 static kstat_t *nfs2_ksp_client;
177 static kstat_t *nfs2_ksp_server;
178 static kstat_t *nfs3_ksp_client;
179 static kstat_t *nfs3_ksp_server;
180 static kstat_t *nfs4_ksp_client;
181 static kstat_t *nfs4_ksp_server;
182 #endif
184 /* Possibly TODO: NFSv4 statistics */
186 #if KERNEL_LINUX
187 static int nfs_init (void)
188 {
189 return (0);
190 }
191 /* #endif KERNEL_LINUX */
193 #elif HAVE_LIBKSTAT
194 static int nfs_init (void)
195 {
196 kstat_t *ksp_chain = NULL;
198 nfs2_ksp_client = NULL;
199 nfs2_ksp_server = NULL;
200 nfs3_ksp_client = NULL;
201 nfs3_ksp_server = NULL;
202 nfs4_ksp_client = NULL;
203 nfs4_ksp_server = NULL;
205 if (kc == NULL)
206 return (-1);
208 for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
209 ksp_chain = ksp_chain->ks_next)
210 {
211 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
212 continue;
213 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
214 nfs2_ksp_server = ksp_chain;
215 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
216 nfs3_ksp_server = ksp_chain;
217 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
218 nfs4_ksp_server = ksp_chain;
219 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
220 nfs2_ksp_client = ksp_chain;
221 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
222 nfs3_ksp_client = ksp_chain;
223 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
224 nfs4_ksp_client = ksp_chain;
225 }
227 return (0);
228 } /* int nfs_init */
229 #endif
231 static void nfs_procedures_submit (const char *plugin_instance,
232 const char **type_instances,
233 value_t *values, size_t values_num)
234 {
235 value_list_t vl = VALUE_LIST_INIT;
236 size_t i;
238 vl.values_len = 1;
239 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
240 sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
241 sstrncpy (vl.plugin_instance, plugin_instance,
242 sizeof (vl.plugin_instance));
243 sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
245 for (i = 0; i < values_num; i++)
246 {
247 vl.values = values + i;
248 sstrncpy (vl.type_instance, type_instances[i],
249 sizeof (vl.type_instance));
250 plugin_dispatch_values (&vl);
251 }
252 } /* void nfs_procedures_submit */
254 #if KERNEL_LINUX
255 static int nfs_submit_fields (int nfs_version, const char *instance,
256 char **fields, size_t fields_num,
257 const char **proc_names, size_t proc_names_num)
258 {
259 char plugin_instance[DATA_MAX_NAME_LEN];
260 value_t values[fields_num];
261 size_t i;
263 if (fields_num != proc_names_num)
264 {
265 WARNING ("nfs plugin: Wrong number of fields for "
266 "NFSv%i %s statistics. Expected %zu, got %zu.",
267 nfs_version, instance,
268 proc_names_num, fields_num);
269 return (EINVAL);
270 }
272 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
273 nfs_version, instance);
275 for (i = 0; i < proc_names_num; i++)
276 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
278 nfs_procedures_submit (plugin_instance, proc_names, values,
279 proc_names_num);
281 return (0);
282 }
284 static void nfs_read_linux (FILE *fh, char *inst)
285 {
286 char buffer[1024];
288 char *fields[48];
289 int fields_num = 0;
291 if (fh == NULL)
292 return;
294 while (fgets (buffer, sizeof (buffer), fh) != NULL)
295 {
296 fields_num = strsplit (buffer,
297 fields, STATIC_ARRAY_SIZE (fields));
299 if (fields_num < 3)
300 continue;
302 if (strcmp (fields[0], "proc2") == 0)
303 {
304 nfs_submit_fields (/* version = */ 2, inst,
305 fields + 2, (size_t) (fields_num - 2),
306 nfs2_procedures_names,
307 nfs2_procedures_names_num);
308 }
309 else if (strncmp (fields[0], "proc3", 5) == 0)
310 {
311 nfs_submit_fields (/* version = */ 3, inst,
312 fields + 2, (size_t) (fields_num - 2),
313 nfs3_procedures_names,
314 nfs3_procedures_names_num);
315 }
316 } /* while (fgets) */
317 } /* void nfs_read_linux */
318 #endif /* KERNEL_LINUX */
320 #if HAVE_LIBKSTAT
321 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *inst,
322 char const **proc_names, size_t proc_names_num)
323 {
324 char plugin_instance[DATA_MAX_NAME_LEN];
325 value_t values[proc_names_num];
326 size_t i;
328 if (ksp == NULL)
329 return (EINVAL);
331 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
332 nfs_version, inst);
334 kstat_read(kc, ksp, NULL);
335 for (i = 0; i < proc_names_num; i++)
336 {
337 /* The name passed to kstat_data_lookup() doesn't have the
338 * "const" modifier, so we need to copy the name here. */
339 char name[32];
340 sstrncpy (name, proc_names[i], sizeof (name));
342 values[i].counter = (derive_t) get_kstat_value (ksp, name);
343 }
345 nfs_procedures_submit (plugin_instance, proc_names, values,
346 proc_names_num);
347 return (0);
348 }
349 #endif
351 #if KERNEL_LINUX
352 static int nfs_read (void)
353 {
354 FILE *fh;
356 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
357 {
358 nfs_read_linux (fh, "client");
359 fclose (fh);
360 }
362 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
363 {
364 nfs_read_linux (fh, "server");
365 fclose (fh);
366 }
368 return (0);
369 }
370 /* #endif KERNEL_LINUX */
372 #elif HAVE_LIBKSTAT
373 static int nfs_read (void)
374 {
375 nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
376 nfs2_procedures_names, nfs2_procedures_names_num);
377 nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
378 nfs2_procedures_names, nfs2_procedures_names_num);
379 nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
380 nfs3_procedures_names, nfs3_procedures_names_num);
381 nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
382 nfs3_procedures_names, nfs3_procedures_names_num);
383 nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
384 nfs4_procedures_names, nfs4_procedures_names_num);
385 nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
386 nfs4_procedures_names, nfs4_procedures_names_num);
388 return (0);
389 }
390 #endif /* HAVE_LIBKSTAT */
392 void module_register (void)
393 {
394 plugin_register_init ("nfs", nfs_init);
395 plugin_register_read ("nfs", nfs_read);
396 } /* void module_register */