Merge branch 'collectd-5.7'
[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.plugin, "nfs", sizeof(vl.plugin));
343   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
344   sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
345
346   for (size_t i = 0; i < values_num; i++) {
347     vl.values = values + i;
348     sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
349     plugin_dispatch_values(&vl);
350   }
351 } /* void nfs_procedures_submit */
352
353 #if KERNEL_LINUX
354 static void nfs_submit_fields(int nfs_version, const char *instance,
355                               char **fields, size_t fields_num,
356                               const char **proc_names) {
357   char plugin_instance[DATA_MAX_NAME_LEN];
358   value_t values[fields_num];
359
360   ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
361             instance);
362
363   for (size_t i = 0; i < fields_num; i++)
364     (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
365
366   nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
367 }
368
369 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
370                                   char **fields, size_t fields_num,
371                                   const char **proc_names,
372                                   size_t proc_names_num) {
373   if (fields_num != proc_names_num) {
374     WARNING("nfs plugin: Wrong number of fields for "
375             "NFSv%i %s statistics. Expected %zu, got %zu.",
376             nfs_version, instance, proc_names_num, fields_num);
377     return (EINVAL);
378   }
379
380   nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
381
382   return (0);
383 }
384
385 static int nfs_submit_nfs4_server(const char *instance, char **fields,
386                                   size_t fields_num) {
387   static int suppress_warning = 0;
388
389   if (fields_num != NFS4_SERVER40_NUM_PROC &&
390       fields_num != NFS4_SERVER41_NUM_PROC) {
391     if (!suppress_warning) {
392       WARNING("nfs plugin: Unexpected number of fields for "
393               "NFSv4 %s statistics: %zu. ",
394               instance, fields_num);
395     }
396
397     if (fields_num > NFS4_SERVER_MAX_PROC) {
398       fields_num = NFS4_SERVER_MAX_PROC;
399       suppress_warning = 1;
400     } else {
401       return (EINVAL);
402     }
403   }
404
405   nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
406                     nfs4_server40_procedures_names);
407
408   if (fields_num >= NFS4_SERVER41_NUM_PROC) {
409     fields += nfs4_server40_procedures_names_num;
410
411     nfs_submit_fields(4, instance, fields, nfs4_server41_procedures_names_num,
412                       nfs4_server41_procedures_names);
413   }
414
415   return (0);
416 }
417
418 static int nfs_submit_nfs4_client(const char *instance, char **fields,
419                                   size_t fields_num) {
420   size_t proc40_names_num, proc41_names_num;
421
422   static int suppress_warning = 0;
423
424   switch (fields_num) {
425   case 34:
426   case 35:
427   case 36:
428   case 37:
429   case 38:
430     /* 4.0-only configuration */
431     proc40_names_num = fields_num;
432     break;
433   case 40:
434   case 41:
435     proc40_names_num = 35;
436     break;
437   case 42:
438   case 44:
439     proc40_names_num = 36;
440     break;
441   case 46:
442   case 47:
443   case 51:
444   case 53:
445     proc40_names_num = 37;
446     break;
447   case 54:
448     proc40_names_num = 38;
449     break;
450   default:
451     if (!suppress_warning) {
452       WARNING("nfs plugin: Unexpected number of "
453               "fields for NFSv4 %s "
454               "statistics: %zu. ",
455               instance, fields_num);
456     }
457
458     if (fields_num > 34) {
459       /* safe fallback to basic nfs40 procedures */
460       fields_num = 34;
461       proc40_names_num = 34;
462
463       suppress_warning = 1;
464     } else {
465       return (EINVAL);
466     }
467   }
468
469   nfs_submit_fields(4, instance, fields, proc40_names_num,
470                     nfs4_client40_procedures_names);
471
472   if (fields_num > proc40_names_num) {
473     proc41_names_num = fields_num - proc40_names_num;
474     fields += proc40_names_num;
475
476     nfs_submit_fields(4, instance, fields, proc41_names_num,
477                       nfs4_client41_procedures_names);
478   }
479
480   return (0);
481 }
482
483 static void nfs_read_linux(FILE *fh, const char *inst) {
484   char buffer[1024];
485
486   char *fields[64];
487   int fields_num = 0;
488
489   if (fh == NULL)
490     return;
491
492   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
493     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
494
495     if (fields_num < 3)
496       continue;
497
498     if (strcmp(fields[0], "proc2") == 0) {
499       nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
500                              (size_t)(fields_num - 2), nfs2_procedures_names,
501                              nfs2_procedures_names_num);
502     } else if (strncmp(fields[0], "proc3", 5) == 0) {
503       nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
504                              (size_t)(fields_num - 2), nfs3_procedures_names,
505                              nfs3_procedures_names_num);
506     } else if (strcmp(fields[0], "proc4ops") == 0) {
507       if (inst[0] == 's')
508         nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
509     } else if (strcmp(fields[0], "proc4") == 0) {
510       if (inst[0] == 'c')
511         nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
512     }
513   } /* while (fgets) */
514 } /* void nfs_read_linux */
515 #endif /* KERNEL_LINUX */
516
517 #if HAVE_LIBKSTAT
518 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
519                           char const **proc_names, size_t proc_names_num) {
520   char plugin_instance[DATA_MAX_NAME_LEN];
521   value_t values[proc_names_num];
522
523   if (ksp == NULL)
524     return (EINVAL);
525
526   ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
527             inst);
528
529   kstat_read(kc, ksp, NULL);
530   for (size_t i = 0; i < proc_names_num; i++) {
531     /* The name passed to kstat_data_lookup() doesn't have the
532      * "const" modifier, so we need to copy the name here. */
533     char name[32];
534     sstrncpy(name, proc_names[i], sizeof(name));
535
536     values[i].counter = (derive_t)get_kstat_value(ksp, name);
537   }
538
539   nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
540   return (0);
541 }
542 #endif
543
544 #if KERNEL_LINUX
545 static int nfs_read(void) {
546   FILE *fh;
547
548   if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
549     nfs_read_linux(fh, "client");
550     fclose(fh);
551   }
552
553   if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
554     nfs_read_linux(fh, "server");
555     fclose(fh);
556   }
557
558   return (0);
559 }
560 /* #endif KERNEL_LINUX */
561
562 #elif HAVE_LIBKSTAT
563 static int nfs_read(void) {
564   nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
565                  nfs2_procedures_names, nfs2_procedures_names_num);
566   nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
567                  nfs2_procedures_names, nfs2_procedures_names_num);
568   nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
569                  nfs3_procedures_names, nfs3_procedures_names_num);
570   nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
571                  nfs3_procedures_names, nfs3_procedures_names_num);
572   nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
573                  nfs4_procedures_names, nfs4_procedures_names_num);
574   nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
575                  nfs4_procedures_names, nfs4_procedures_names_num);
576
577   return (0);
578 }
579 #endif /* HAVE_LIBKSTAT */
580
581 void module_register(void) {
582   plugin_register_init("nfs", nfs_init);
583   plugin_register_read("nfs", nfs_read);
584 } /* void module_register */