Merge branch 'collectd-5.5' into collectd-5.6
[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  **/
24
25 #include "collectd.h"
26
27 #include "common.h"
28 #include "plugin.h"
29
30 #if HAVE_KSTAT_H
31 #include <kstat.h>
32 #endif
33
34 /*
35 see /proc/net/rpc/nfs
36 see http://www.missioncriticallinux.com/orph/NFS-Statistics
37
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.
43
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.
48
49 proc2 x x x...
50 proc3 x x x...
51
52 Procedure   NFS Version NFS Version 3
53 Number      Procedures  Procedures
54
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 */
78
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);
85
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);
93
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
137
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"};
179
180 static size_t nfs4_server40_procedures_names_num =
181     STATIC_ARRAY_SIZE(nfs4_server40_procedures_names);
182
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 };
204
205 static size_t nfs4_server41_procedures_names_num =
206     STATIC_ARRAY_SIZE(nfs4_server41_procedures_names);
207
208 #define NFS4_SERVER40_NUM_PROC                                                 \
209   (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names))
210
211 #define NFS4_SERVER41_NUM_PROC                                                 \
212   (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names) +                         \
213    STATIC_ARRAY_SIZE(nfs4_server41_procedures_names))
214
215 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
216
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 };
257
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 };
276
277 #define NFS4_CLIENT40_NUM_PROC                                                 \
278   (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names))
279
280 #define NFS4_CLIENT41_NUM_PROC                                                 \
281   (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names) +                         \
282    STATIC_ARRAY_SIZE(nfs4_client41_procedures_names))
283
284 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
285
286 #endif
287
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
297
298 #if KERNEL_LINUX
299 static int nfs_init(void) { return (0); }
300 /* #endif KERNEL_LINUX */
301
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;
310
311   if (kc == NULL)
312     return (-1);
313
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   }
331
332   return (0);
333 } /* int nfs_init */
334 #endif
335
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;
340
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));
346
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 */
353
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];
360
361   ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
362             instance);
363
364   for (size_t i = 0; i < fields_num; i++)
365     (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
366
367   nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
368 }
369
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   }
380
381   nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
382
383   return (0);
384 }
385
386 static int nfs_submit_nfs4_server(const char *instance, char **fields,
387                                   size_t fields_num) {
388   static int suppress_warning = 0;
389
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     }
397
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   }
405
406   nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
407                     nfs4_server40_procedures_names);
408
409   if (fields_num >= NFS4_SERVER41_NUM_PROC) {
410     fields += nfs4_server40_procedures_names_num;
411
412     nfs_submit_fields(4, instance, fields, nfs4_server41_procedures_names_num,
413                       nfs4_server41_procedures_names);
414   }
415
416   return (0);
417 }
418
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;
422
423   static int suppress_warning = 0;
424
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     }
458
459     if (fields_num > 34) {
460       /* safe fallback to basic nfs40 procedures */
461       fields_num = 34;
462       proc40_names_num = 34;
463
464       suppress_warning = 1;
465     } else {
466       return (EINVAL);
467     }
468   }
469
470   nfs_submit_fields(4, instance, fields, proc40_names_num,
471                     nfs4_client40_procedures_names);
472
473   if (fields_num > proc40_names_num) {
474     proc41_names_num = fields_num - proc40_names_num;
475     fields += proc40_names_num;
476
477     nfs_submit_fields(4, instance, fields, proc41_names_num,
478                       nfs4_client41_procedures_names);
479   }
480
481   return (0);
482 }
483
484 static void nfs_read_linux(FILE *fh, const char *inst) {
485   char buffer[1024];
486
487   char *fields[64];
488   int fields_num = 0;
489
490   if (fh == NULL)
491     return;
492
493   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
494     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
495
496     if (fields_num < 3)
497       continue;
498
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 */
517
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];
523
524   if (ksp == NULL)
525     return (EINVAL);
526
527   ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
528             inst);
529
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));
536
537     values[i].counter = (derive_t)get_kstat_value(ksp, name);
538   }
539
540   nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
541   return (0);
542 }
543 #endif
544
545 #if KERNEL_LINUX
546 static int nfs_read(void) {
547   FILE *fh;
548
549   if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
550     nfs_read_linux(fh, "client");
551     fclose(fh);
552   }
553
554   if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
555     nfs_read_linux(fh, "server");
556     fclose(fh);
557   }
558
559   return (0);
560 }
561 /* #endif KERNEL_LINUX */
562
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);
577
578   return (0);
579 }
580 #endif /* HAVE_LIBKSTAT */
581
582 void module_register(void) {
583   plugin_register_init("nfs", nfs_init);
584   plugin_register_read("nfs", nfs_read);
585 } /* void module_register */