Merge branch 'collectd-5.4' into collectd-5.5
[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 #include "common.h"
27 #include "plugin.h"
28
29 #if HAVE_KSTAT_H
30 #include <kstat.h>
31 #endif
32
33 /*
34 see /proc/net/rpc/nfs
35 see http://www.missioncriticallinux.com/orph/NFS-Statistics
36
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.
42
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.
47
48 proc2 x x x...
49 proc3 x x x...
50
51 Procedure   NFS Version NFS Version 3
52 Number      Procedures  Procedures
53
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 */
77
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);
100
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);
127
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
173
174 #if KERNEL_LINUX
175 static const char *nfs4_server40_procedures_names[] =
176 {
177         "null",
178         "compound",
179         "reserved",
180         "access",
181         "close",
182         "commit",
183         "create",
184         "delegpurge",
185         "delegreturn",
186         "getattr",
187         "getfh",
188         "link",
189         "lock",
190         "lockt",
191         "locku",
192         "lookup",
193         "lookupp",
194         "nverify",
195         "open",
196         "openattr",
197         "open_confirm",
198         "open_downgrade",
199         "putfh",
200         "putpubfh",
201         "putrootfh",
202         "read",
203         "readdir",
204         "readlink",
205         "remove",
206         "rename",
207         "renew",
208         "restorefh",
209         "savefh",
210         "secinfo",
211         "setattr",
212         "setclientid",
213         "setcltid_confirm",
214         "verify",
215         "write",
216         "release_lockowner"
217 };
218
219 static size_t nfs4_server40_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server40_procedures_names);
220
221 static const char *nfs4_server41_procedures_names[] =
222 {
223         "backchannel_ctl",
224         "bind_conn_to_session", 
225         "exchange_id",
226         "create_session",
227         "destroy_session",
228         "free_stateid",
229         "get_dir_delegation",
230         "getdeviceinfo",
231         "getdevicelist",
232         "layoutcommit",
233         "layoutget",
234         "layoutreturn",
235         "secinfo_no_name",
236         "sequence",
237         "set_ssv",
238         "test_stateid",
239         "want_delegation",
240         "destroy_clientid",
241         "reclaim_complete",
242 };
243
244 static size_t nfs4_server41_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server41_procedures_names);
245
246 #define NFS4_SERVER40_NUM_PROC ( \
247         STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) )
248
249 #define NFS4_SERVER41_NUM_PROC ( \
250         STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) + \
251         STATIC_ARRAY_SIZE (nfs4_server41_procedures_names) )
252
253 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
254
255 static const char *nfs4_client40_procedures_names[] =
256 {
257         "null",
258         "read",
259         "write",
260         "commit",
261         "open",
262         "open_confirm",
263         "open_noattr",
264         "open_downgrade",
265         "close",
266         "setattr",
267         "fsinfo",
268         "renew",
269         "setclientid",
270         "setclientid_confirm",
271         "lock",
272         "lockt",
273         "locku",
274         "access",
275         "getattr",
276         "lookup",
277         "lookupp",
278         "remove",
279         "rename",
280         "link",
281         "symlink",
282         "create",
283         "pathconf",
284         "statfs",
285         "readlink",
286         "readdir",
287         "server_caps",
288         "delegreturn",
289         "getacl",
290         "setacl",
291         "fs_locations",         /* |35| 2.6.18 */
292         "release_lockowner",    /* |42| 2.6.36 */
293         "secinfo",              /* |46| 2.6.39 */
294         "fsid_present"          /* |54| 3.13 */
295 };
296
297 static const char *nfs4_client41_procedures_names[] =
298 {
299         "exchange_id",          /* |40| 2.6.30 */
300         "create_session",       /* |40| 2.6.30 */
301         "destroy_session",      /* |40| 2.6.30 */
302         "sequence",             /* |40| 2.6.30 */
303         "get_lease_time",       /* |40| 2.6.30 */
304         "reclaim_complete",     /* |41| 2.6.33 */
305         "layoutget",            /* |44| 2.6.37 */
306         "getdeviceinfo",        /* |44| 2.6.37 */
307         "layoutcommit",         /* |46| 2.6.39 */
308         "layoutreturn",         /* |47| 3.0 */
309         "secinfo_no_name",      /* |51| 3.1 */
310         "test_stateid",         /* |51| 3.1 */
311         "free_stateid",         /* |51| 3.1 */
312         "getdevicelist",        /* |51| 3.1 */
313         "bind_conn_to_session", /* |53| 3.5 */
314         "destroy_clientid"      /* |53| 3.5 */
315 };
316
317 #define NFS4_CLIENT40_NUM_PROC ( \
318         STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) )
319
320 #define NFS4_CLIENT41_NUM_PROC ( \
321         STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) + \
322         STATIC_ARRAY_SIZE (nfs4_client41_procedures_names) )
323
324 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
325
326 #endif
327
328 #if HAVE_LIBKSTAT
329 extern kstat_ctl_t *kc;
330 static kstat_t *nfs2_ksp_client;
331 static kstat_t *nfs2_ksp_server;
332 static kstat_t *nfs3_ksp_client;
333 static kstat_t *nfs3_ksp_server;
334 static kstat_t *nfs4_ksp_client;
335 static kstat_t *nfs4_ksp_server;
336 #endif
337
338 #if KERNEL_LINUX
339 static int nfs_init (void)
340 {
341         return (0);
342 }
343 /* #endif KERNEL_LINUX */
344
345 #elif HAVE_LIBKSTAT
346 static int nfs_init (void)
347 {
348         kstat_t *ksp_chain = NULL;
349
350         nfs2_ksp_client = NULL;
351         nfs2_ksp_server = NULL;
352         nfs3_ksp_client = NULL;
353         nfs3_ksp_server = NULL;
354         nfs4_ksp_client = NULL;
355         nfs4_ksp_server = NULL;
356
357         if (kc == NULL)
358                 return (-1);
359
360         for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
361                         ksp_chain = ksp_chain->ks_next)
362         {
363                 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
364                         continue;
365                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
366                         nfs2_ksp_server = ksp_chain;
367                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
368                         nfs3_ksp_server = ksp_chain;
369                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
370                         nfs4_ksp_server = ksp_chain;
371                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
372                         nfs2_ksp_client = ksp_chain;
373                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
374                         nfs3_ksp_client = ksp_chain;
375                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
376                         nfs4_ksp_client = ksp_chain;
377         }
378
379         return (0);
380 } /* int nfs_init */
381 #endif
382
383 static void nfs_procedures_submit (const char *plugin_instance,
384                 const char **type_instances,
385                 value_t *values, size_t values_num)
386 {
387         value_list_t vl = VALUE_LIST_INIT;
388         size_t i;
389
390         vl.values_len = 1;
391         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
392         sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
393         sstrncpy (vl.plugin_instance, plugin_instance,
394                         sizeof (vl.plugin_instance));
395         sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
396
397         for (i = 0; i < values_num; i++)
398         {
399                 vl.values = values + i;
400                 sstrncpy (vl.type_instance, type_instances[i],
401                                 sizeof (vl.type_instance));
402                 plugin_dispatch_values (&vl);
403         }
404 } /* void nfs_procedures_submit */
405
406 #if KERNEL_LINUX
407 static void nfs_submit_fields (int nfs_version, const char *instance, 
408                 char **fields, size_t fields_num, const char **proc_names)
409 {
410         char plugin_instance[DATA_MAX_NAME_LEN];
411         value_t values[fields_num];
412         size_t i;
413
414         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
415                         nfs_version, instance);
416
417         for (i = 0; i < fields_num; i++)
418                 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
419
420         nfs_procedures_submit (plugin_instance, proc_names, values,
421                         fields_num);
422 }
423
424 static int nfs_submit_fields_safe (int nfs_version, const char *instance,
425                 char **fields, size_t fields_num,
426                 const char **proc_names, size_t proc_names_num)
427 {
428         if (fields_num != proc_names_num)
429         {
430                 WARNING ("nfs plugin: Wrong number of fields for "
431                                 "NFSv%i %s statistics. Expected %zu, got %zu.",
432                                 nfs_version, instance,
433                                 proc_names_num, fields_num);
434                 return (EINVAL);
435         }
436
437         nfs_submit_fields (nfs_version, instance, fields, fields_num, 
438                         proc_names);
439
440         return (0);
441 }
442
443 static int nfs_submit_nfs4_server (const char *instance, char **fields, 
444                 size_t fields_num)
445 {
446         static int suppress_warning = 0;
447
448         if (fields_num != NFS4_SERVER40_NUM_PROC &&
449                 fields_num != NFS4_SERVER41_NUM_PROC) 
450         {
451                 if (!suppress_warning)
452                 {
453                         WARNING ("nfs plugin: Unexpected number of fields for "
454                                         "NFSv4 %s statistics: %zu. ",
455                                         instance, fields_num);
456                 }
457
458                 if (fields_num > NFS4_SERVER_MAX_PROC)
459                 {
460                         fields_num = NFS4_SERVER_MAX_PROC;
461                         suppress_warning = 1;
462                 }
463                 else
464                 {
465                         return (EINVAL);
466                 }
467         }
468
469         nfs_submit_fields (4, instance, fields, 
470                         nfs4_server40_procedures_names_num,
471                         nfs4_server40_procedures_names);
472
473         if (fields_num >= NFS4_SERVER41_NUM_PROC)
474         {
475                 fields += nfs4_server40_procedures_names_num;
476
477                 nfs_submit_fields (4, instance, fields, 
478                                 nfs4_server41_procedures_names_num, 
479                                 nfs4_server41_procedures_names);
480         }
481
482         return (0);
483 }
484
485 static int nfs_submit_nfs4_client (const char *instance, char **fields, 
486                 size_t fields_num)
487 {
488         size_t proc40_names_num, proc41_names_num;
489
490         static int suppress_warning = 0;
491
492         switch (fields_num)
493         {
494                 case 34:
495                 case 35:
496                 case 36:
497                 case 37:
498                 case 38:
499                         /* 4.0-only configuration */
500                         proc40_names_num = fields_num;
501                         break;
502                 case 40:
503                 case 41:
504                         proc40_names_num = 35;
505                         break;
506                 case 42:
507                 case 44:
508                         proc40_names_num = 36;
509                         break;
510                 case 46:
511                 case 47:
512                 case 51:
513                 case 53:
514                         proc40_names_num = 37;
515                         break;
516                 case 54:
517                         proc40_names_num = 38;
518                         break;
519                 default:
520                         if (!suppress_warning)
521                         {
522                                 WARNING ("nfs plugin: Unexpected number of "
523                                                 "fields for NFSv4 %s "
524                                                 "statistics: %zu. ",
525                                                 instance, fields_num);
526                         }
527
528                         if (fields_num > 34)
529                         {
530                                 /* safe fallback to basic nfs40 procedures */
531                                 fields_num = 34;
532                                 proc40_names_num = 34;
533
534                                 suppress_warning = 1;
535                         }
536                         else
537                         {
538                                 return (EINVAL);
539                         }
540         }
541
542         nfs_submit_fields (4, instance, fields, proc40_names_num,
543                         nfs4_client40_procedures_names);
544
545         if (fields_num > proc40_names_num)
546         {
547                 proc41_names_num = fields_num - proc40_names_num;
548                 fields += proc40_names_num;
549
550                 nfs_submit_fields (4, instance, fields,proc41_names_num,
551                                  nfs4_client41_procedures_names);
552         }
553
554         return (0);
555 }
556
557 static void nfs_read_linux (FILE *fh, char *inst)
558 {
559         char buffer[1024];
560
561         char *fields[64];
562         int fields_num = 0;
563
564         if (fh == NULL)
565                 return;
566
567         while (fgets (buffer, sizeof (buffer), fh) != NULL)
568         {
569                 fields_num = strsplit (buffer,
570                                 fields, STATIC_ARRAY_SIZE (fields));
571
572                 if (fields_num < 3)
573                         continue;
574
575                 if (strcmp (fields[0], "proc2") == 0)
576                 {
577                         nfs_submit_fields_safe (/* version = */ 2, inst,
578                                         fields + 2, (size_t) (fields_num - 2),
579                                         nfs2_procedures_names,
580                                         nfs2_procedures_names_num);
581                 }
582                 else if (strncmp (fields[0], "proc3", 5) == 0)
583                 {
584                         nfs_submit_fields_safe (/* version = */ 3, inst,
585                                         fields + 2, (size_t) (fields_num - 2),
586                                         nfs3_procedures_names,
587                                         nfs3_procedures_names_num);
588                 }
589                 else if (strcmp (fields[0], "proc4ops") == 0)
590                 {
591                         if (inst[0] == 's')
592                                 nfs_submit_nfs4_server (inst, fields + 2, 
593                                                 (size_t) (fields_num - 2));
594                 }
595                 else if (strcmp (fields[0], "proc4") == 0)
596                 {
597                         if (inst[0] == 'c')
598                                 nfs_submit_nfs4_client (inst, fields + 2,
599                                                 (size_t) (fields_num - 2));                     
600                 }
601         } /* while (fgets) */
602 } /* void nfs_read_linux */
603 #endif /* KERNEL_LINUX */
604
605 #if HAVE_LIBKSTAT
606 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *inst,
607                 char const **proc_names, size_t proc_names_num)
608 {
609         char plugin_instance[DATA_MAX_NAME_LEN];
610         value_t values[proc_names_num];
611         size_t i;
612
613         if (ksp == NULL)
614                 return (EINVAL);
615
616         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
617                         nfs_version, inst);
618
619         kstat_read(kc, ksp, NULL);
620         for (i = 0; i < proc_names_num; i++)
621         {
622                 /* The name passed to kstat_data_lookup() doesn't have the
623                  * "const" modifier, so we need to copy the name here. */
624                 char name[32];
625                 sstrncpy (name, proc_names[i], sizeof (name));
626
627                 values[i].counter = (derive_t) get_kstat_value (ksp, name);
628         }
629
630         nfs_procedures_submit (plugin_instance, proc_names, values,
631                         proc_names_num);
632         return (0);
633 }
634 #endif
635
636 #if KERNEL_LINUX
637 static int nfs_read (void)
638 {
639         FILE *fh;
640
641         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
642         {
643                 nfs_read_linux (fh, "client");
644                 fclose (fh);
645         }
646
647         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
648         {
649                 nfs_read_linux (fh, "server");
650                 fclose (fh);
651         }
652
653         return (0);
654 }
655 /* #endif KERNEL_LINUX */
656
657 #elif HAVE_LIBKSTAT
658 static int nfs_read (void)
659 {
660         nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
661                         nfs2_procedures_names, nfs2_procedures_names_num);
662         nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
663                         nfs2_procedures_names, nfs2_procedures_names_num);
664         nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
665                         nfs3_procedures_names, nfs3_procedures_names_num);
666         nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
667                         nfs3_procedures_names, nfs3_procedures_names_num);
668         nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
669                         nfs4_procedures_names, nfs4_procedures_names_num);
670         nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
671                         nfs4_procedures_names, nfs4_procedures_names_num);
672
673         return (0);
674 }
675 #endif /* HAVE_LIBKSTAT */
676
677 void module_register (void)
678 {
679         plugin_register_init ("nfs", nfs_init);
680         plugin_register_read ("nfs", nfs_read);
681 } /* void module_register */