Code

Merge pull request #2278 from octo/ff/perl
[collectd.git] / src / nfs.c
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"
27 #include "common.h"
28 #include "plugin.h"
30 #if HAVE_KSTAT_H
31 #include <kstat.h>
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 const char *nfs2_procedures_names[] = {
80     "null", "getattr", "setattr", "root",   "lookup",  "readlink",
81     "read", "wrcache", "write",   "create", "remove",  "rename",
82     "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"};
83 static size_t nfs2_procedures_names_num =
84     STATIC_ARRAY_SIZE(nfs2_procedures_names);
86 static const char *nfs3_procedures_names[] = {
87     "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
88     "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
89     "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
90     "fsstat", "fsinfo",  "pathconf", "commit"};
91 static size_t nfs3_procedures_names_num =
92     STATIC_ARRAY_SIZE(nfs3_procedures_names);
94 #if HAVE_LIBKSTAT
95 static const char *nfs4_procedures_names[] = {"null",
96                                               "compound",
97                                               "reserved",
98                                               "access",
99                                               "close",
100                                               "commit",
101                                               "create",
102                                               "delegpurge",
103                                               "delegreturn",
104                                               "getattr",
105                                               "getfh",
106                                               "link",
107                                               "lock",
108                                               "lockt",
109                                               "locku",
110                                               "lookup",
111                                               "lookupp",
112                                               "nverify",
113                                               "open",
114                                               "openattr",
115                                               "open_confirm",
116                                               "open_downgrade",
117                                               "putfh",
118                                               "putpubfh",
119                                               "putrootfh",
120                                               "read",
121                                               "readdir",
122                                               "readlink",
123                                               "remove",
124                                               "rename",
125                                               "renew",
126                                               "restorefh",
127                                               "savefh",
128                                               "secinfo",
129                                               "setattr",
130                                               "setclientid",
131                                               "setclientid_confirm",
132                                               "verify",
133                                               "write"};
134 static size_t nfs4_procedures_names_num =
135     STATIC_ARRAY_SIZE(nfs4_procedures_names);
136 #endif
138 #if KERNEL_LINUX
139 static const char *nfs4_server40_procedures_names[] = {"null",
140                                                        "compound",
141                                                        "reserved",
142                                                        "access",
143                                                        "close",
144                                                        "commit",
145                                                        "create",
146                                                        "delegpurge",
147                                                        "delegreturn",
148                                                        "getattr",
149                                                        "getfh",
150                                                        "link",
151                                                        "lock",
152                                                        "lockt",
153                                                        "locku",
154                                                        "lookup",
155                                                        "lookupp",
156                                                        "nverify",
157                                                        "open",
158                                                        "openattr",
159                                                        "open_confirm",
160                                                        "open_downgrade",
161                                                        "putfh",
162                                                        "putpubfh",
163                                                        "putrootfh",
164                                                        "read",
165                                                        "readdir",
166                                                        "readlink",
167                                                        "remove",
168                                                        "rename",
169                                                        "renew",
170                                                        "restorefh",
171                                                        "savefh",
172                                                        "secinfo",
173                                                        "setattr",
174                                                        "setclientid",
175                                                        "setcltid_confirm",
176                                                        "verify",
177                                                        "write",
178                                                        "release_lockowner"};
180 static size_t nfs4_server40_procedures_names_num =
181     STATIC_ARRAY_SIZE(nfs4_server40_procedures_names);
183 static const char *nfs4_server41_procedures_names[] = {
184     "backchannel_ctl",
185     "bind_conn_to_session",
186     "exchange_id",
187     "create_session",
188     "destroy_session",
189     "free_stateid",
190     "get_dir_delegation",
191     "getdeviceinfo",
192     "getdevicelist",
193     "layoutcommit",
194     "layoutget",
195     "layoutreturn",
196     "secinfo_no_name",
197     "sequence",
198     "set_ssv",
199     "test_stateid",
200     "want_delegation",
201     "destroy_clientid",
202     "reclaim_complete",
203 };
205 static size_t nfs4_server41_procedures_names_num =
206     STATIC_ARRAY_SIZE(nfs4_server41_procedures_names);
208 #define NFS4_SERVER40_NUM_PROC                                                 \
209   (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names))
211 #define NFS4_SERVER41_NUM_PROC                                                 \
212   (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names) +                         \
213    STATIC_ARRAY_SIZE(nfs4_server41_procedures_names))
215 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
217 static const char *nfs4_client40_procedures_names[] = {
218     "null",
219     "read",
220     "write",
221     "commit",
222     "open",
223     "open_confirm",
224     "open_noattr",
225     "open_downgrade",
226     "close",
227     "setattr",
228     "fsinfo",
229     "renew",
230     "setclientid",
231     "setclientid_confirm",
232     "lock",
233     "lockt",
234     "locku",
235     "access",
236     "getattr",
237     "lookup",
238     "lookupp",
239     "remove",
240     "rename",
241     "link",
242     "symlink",
243     "create",
244     "pathconf",
245     "statfs",
246     "readlink",
247     "readdir",
248     "server_caps",
249     "delegreturn",
250     "getacl",
251     "setacl",
252     "fs_locations",      /* |35| 2.6.18 */
253     "release_lockowner", /* |42| 2.6.36 */
254     "secinfo",           /* |46| 2.6.39 */
255     "fsid_present"       /* |54| 3.13 */
256 };
258 static const char *nfs4_client41_procedures_names[] = {
259     "exchange_id",          /* |40| 2.6.30 */
260     "create_session",       /* |40| 2.6.30 */
261     "destroy_session",      /* |40| 2.6.30 */
262     "sequence",             /* |40| 2.6.30 */
263     "get_lease_time",       /* |40| 2.6.30 */
264     "reclaim_complete",     /* |41| 2.6.33 */
265     "layoutget",            /* |44| 2.6.37 */
266     "getdeviceinfo",        /* |44| 2.6.37 */
267     "layoutcommit",         /* |46| 2.6.39 */
268     "layoutreturn",         /* |47| 3.0 */
269     "secinfo_no_name",      /* |51| 3.1 */
270     "test_stateid",         /* |51| 3.1 */
271     "free_stateid",         /* |51| 3.1 */
272     "getdevicelist",        /* |51| 3.1 */
273     "bind_conn_to_session", /* |53| 3.5 */
274     "destroy_clientid"      /* |53| 3.5 */
275 };
277 #define NFS4_CLIENT40_NUM_PROC                                                 \
278   (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names))
280 #define NFS4_CLIENT41_NUM_PROC                                                 \
281   (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names) +                         \
282    STATIC_ARRAY_SIZE(nfs4_client41_procedures_names))
284 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
286 #endif
288 #if HAVE_LIBKSTAT
289 extern kstat_ctl_t *kc;
290 static kstat_t *nfs2_ksp_client;
291 static kstat_t *nfs2_ksp_server;
292 static kstat_t *nfs3_ksp_client;
293 static kstat_t *nfs3_ksp_server;
294 static kstat_t *nfs4_ksp_client;
295 static kstat_t *nfs4_ksp_server;
296 #endif
298 #if KERNEL_LINUX
299 static int nfs_init(void) { return (0); }
300 /* #endif KERNEL_LINUX */
302 #elif HAVE_LIBKSTAT
303 static int nfs_init(void) {
304   nfs2_ksp_client = NULL;
305   nfs2_ksp_server = NULL;
306   nfs3_ksp_client = NULL;
307   nfs3_ksp_server = NULL;
308   nfs4_ksp_client = NULL;
309   nfs4_ksp_server = NULL;
311   if (kc == NULL)
312     return (-1);
314   for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
315        ksp_chain = ksp_chain->ks_next) {
316     if (strncmp(ksp_chain->ks_module, "nfs", 3) != 0)
317       continue;
318     else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
319       nfs2_ksp_server = ksp_chain;
320     else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
321       nfs3_ksp_server = ksp_chain;
322     else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
323       nfs4_ksp_server = ksp_chain;
324     else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
325       nfs2_ksp_client = ksp_chain;
326     else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
327       nfs3_ksp_client = ksp_chain;
328     else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
329       nfs4_ksp_client = ksp_chain;
330   }
332   return (0);
333 } /* int nfs_init */
334 #endif
336 static void nfs_procedures_submit(const char *plugin_instance,
337                                   const char **type_instances, value_t *values,
338                                   size_t values_num) {
339   value_list_t vl = VALUE_LIST_INIT;
341   vl.values_len = 1;
342   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
343   sstrncpy(vl.plugin, "nfs", sizeof(vl.plugin));
344   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
345   sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
347   for (size_t i = 0; i < values_num; i++) {
348     vl.values = values + i;
349     sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
350     plugin_dispatch_values(&vl);
351   }
352 } /* void nfs_procedures_submit */
354 #if KERNEL_LINUX
355 static void nfs_submit_fields(int nfs_version, const char *instance,
356                               char **fields, size_t fields_num,
357                               const char **proc_names) {
358   char plugin_instance[DATA_MAX_NAME_LEN];
359   value_t values[fields_num];
361   ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
362             instance);
364   for (size_t i = 0; i < fields_num; i++)
365     (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
367   nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
370 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
371                                   char **fields, size_t fields_num,
372                                   const char **proc_names,
373                                   size_t proc_names_num) {
374   if (fields_num != proc_names_num) {
375     WARNING("nfs plugin: Wrong number of fields for "
376             "NFSv%i %s statistics. Expected %zu, got %zu.",
377             nfs_version, instance, proc_names_num, fields_num);
378     return (EINVAL);
379   }
381   nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
383   return (0);
386 static int nfs_submit_nfs4_server(const char *instance, char **fields,
387                                   size_t fields_num) {
388   static int suppress_warning = 0;
390   if (fields_num != NFS4_SERVER40_NUM_PROC &&
391       fields_num != NFS4_SERVER41_NUM_PROC) {
392     if (!suppress_warning) {
393       WARNING("nfs plugin: Unexpected number of fields for "
394               "NFSv4 %s statistics: %zu. ",
395               instance, fields_num);
396     }
398     if (fields_num > NFS4_SERVER_MAX_PROC) {
399       fields_num = NFS4_SERVER_MAX_PROC;
400       suppress_warning = 1;
401     } else {
402       return (EINVAL);
403     }
404   }
406   nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
407                     nfs4_server40_procedures_names);
409   if (fields_num >= NFS4_SERVER41_NUM_PROC) {
410     fields += nfs4_server40_procedures_names_num;
412     nfs_submit_fields(4, instance, fields, nfs4_server41_procedures_names_num,
413                       nfs4_server41_procedures_names);
414   }
416   return (0);
419 static int nfs_submit_nfs4_client(const char *instance, char **fields,
420                                   size_t fields_num) {
421   size_t proc40_names_num, proc41_names_num;
423   static int suppress_warning = 0;
425   switch (fields_num) {
426   case 34:
427   case 35:
428   case 36:
429   case 37:
430   case 38:
431     /* 4.0-only configuration */
432     proc40_names_num = fields_num;
433     break;
434   case 40:
435   case 41:
436     proc40_names_num = 35;
437     break;
438   case 42:
439   case 44:
440     proc40_names_num = 36;
441     break;
442   case 46:
443   case 47:
444   case 51:
445   case 53:
446     proc40_names_num = 37;
447     break;
448   case 54:
449     proc40_names_num = 38;
450     break;
451   default:
452     if (!suppress_warning) {
453       WARNING("nfs plugin: Unexpected number of "
454               "fields for NFSv4 %s "
455               "statistics: %zu. ",
456               instance, fields_num);
457     }
459     if (fields_num > 34) {
460       /* safe fallback to basic nfs40 procedures */
461       fields_num = 34;
462       proc40_names_num = 34;
464       suppress_warning = 1;
465     } else {
466       return (EINVAL);
467     }
468   }
470   nfs_submit_fields(4, instance, fields, proc40_names_num,
471                     nfs4_client40_procedures_names);
473   if (fields_num > proc40_names_num) {
474     proc41_names_num = fields_num - proc40_names_num;
475     fields += proc40_names_num;
477     nfs_submit_fields(4, instance, fields, proc41_names_num,
478                       nfs4_client41_procedures_names);
479   }
481   return (0);
484 static void nfs_read_linux(FILE *fh, const char *inst) {
485   char buffer[1024];
487   char *fields[64];
488   int fields_num = 0;
490   if (fh == NULL)
491     return;
493   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
494     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
496     if (fields_num < 3)
497       continue;
499     if (strcmp(fields[0], "proc2") == 0) {
500       nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
501                              (size_t)(fields_num - 2), nfs2_procedures_names,
502                              nfs2_procedures_names_num);
503     } else if (strncmp(fields[0], "proc3", 5) == 0) {
504       nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
505                              (size_t)(fields_num - 2), nfs3_procedures_names,
506                              nfs3_procedures_names_num);
507     } else if (strcmp(fields[0], "proc4ops") == 0) {
508       if (inst[0] == 's')
509         nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
510     } else if (strcmp(fields[0], "proc4") == 0) {
511       if (inst[0] == 'c')
512         nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
513     }
514   } /* while (fgets) */
515 } /* void nfs_read_linux */
516 #endif /* KERNEL_LINUX */
518 #if HAVE_LIBKSTAT
519 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
520                           char const **proc_names, size_t proc_names_num) {
521   char plugin_instance[DATA_MAX_NAME_LEN];
522   value_t values[proc_names_num];
524   if (ksp == NULL)
525     return (EINVAL);
527   ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
528             inst);
530   kstat_read(kc, ksp, NULL);
531   for (size_t i = 0; i < proc_names_num; i++) {
532     /* The name passed to kstat_data_lookup() doesn't have the
533      * "const" modifier, so we need to copy the name here. */
534     char name[32];
535     sstrncpy(name, proc_names[i], sizeof(name));
537     values[i].counter = (derive_t)get_kstat_value(ksp, name);
538   }
540   nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
541   return (0);
543 #endif
545 #if KERNEL_LINUX
546 static int nfs_read(void) {
547   FILE *fh;
549   if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
550     nfs_read_linux(fh, "client");
551     fclose(fh);
552   }
554   if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
555     nfs_read_linux(fh, "server");
556     fclose(fh);
557   }
559   return (0);
561 /* #endif KERNEL_LINUX */
563 #elif HAVE_LIBKSTAT
564 static int nfs_read(void) {
565   nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
566                  nfs2_procedures_names, nfs2_procedures_names_num);
567   nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
568                  nfs2_procedures_names, nfs2_procedures_names_num);
569   nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
570                  nfs3_procedures_names, nfs3_procedures_names_num);
571   nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
572                  nfs3_procedures_names, nfs3_procedures_names_num);
573   nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
574                  nfs4_procedures_names, nfs4_procedures_names_num);
575   nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
576                  nfs4_procedures_names, nfs4_procedures_names_num);
578   return (0);
580 #endif /* HAVE_LIBKSTAT */
582 void module_register(void) {
583   plugin_register_init("nfs", nfs_init);
584   plugin_register_read("nfs", nfs_read);
585 } /* void module_register */