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