Merge branch 'pr/1649'
[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.host, hostname_g, sizeof (vl.host));
390         sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
391         sstrncpy (vl.plugin_instance, plugin_instance,
392                         sizeof (vl.plugin_instance));
393         sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
394
395         for (size_t i = 0; i < values_num; i++)
396         {
397                 vl.values = values + i;
398                 sstrncpy (vl.type_instance, type_instances[i],
399                                 sizeof (vl.type_instance));
400                 plugin_dispatch_values (&vl);
401         }
402 } /* void nfs_procedures_submit */
403
404 #if KERNEL_LINUX
405 static void nfs_submit_fields (int nfs_version, const char *instance,
406                 char **fields, size_t fields_num, const char **proc_names)
407 {
408         char plugin_instance[DATA_MAX_NAME_LEN];
409         value_t values[fields_num];
410
411         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
412                         nfs_version, instance);
413
414         for (size_t i = 0; i < fields_num; i++)
415                 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
416
417         nfs_procedures_submit (plugin_instance, proc_names, values,
418                         fields_num);
419 }
420
421 static int nfs_submit_fields_safe (int nfs_version, const char *instance,
422                 char **fields, size_t fields_num,
423                 const char **proc_names, size_t proc_names_num)
424 {
425         if (fields_num != proc_names_num)
426         {
427                 WARNING ("nfs plugin: Wrong number of fields for "
428                                 "NFSv%i %s statistics. Expected %zu, got %zu.",
429                                 nfs_version, instance,
430                                 proc_names_num, fields_num);
431                 return (EINVAL);
432         }
433
434         nfs_submit_fields (nfs_version, instance, fields, fields_num,
435                         proc_names);
436
437         return (0);
438 }
439
440 static int nfs_submit_nfs4_server (const char *instance, char **fields,
441                 size_t fields_num)
442 {
443         static int suppress_warning = 0;
444
445         if (fields_num != NFS4_SERVER40_NUM_PROC &&
446                 fields_num != NFS4_SERVER41_NUM_PROC)
447         {
448                 if (!suppress_warning)
449                 {
450                         WARNING ("nfs plugin: Unexpected number of fields for "
451                                         "NFSv4 %s statistics: %zu. ",
452                                         instance, fields_num);
453                 }
454
455                 if (fields_num > NFS4_SERVER_MAX_PROC)
456                 {
457                         fields_num = NFS4_SERVER_MAX_PROC;
458                         suppress_warning = 1;
459                 }
460                 else
461                 {
462                         return (EINVAL);
463                 }
464         }
465
466         nfs_submit_fields (4, instance, fields,
467                         nfs4_server40_procedures_names_num,
468                         nfs4_server40_procedures_names);
469
470         if (fields_num >= NFS4_SERVER41_NUM_PROC)
471         {
472                 fields += nfs4_server40_procedures_names_num;
473
474                 nfs_submit_fields (4, instance, fields,
475                                 nfs4_server41_procedures_names_num,
476                                 nfs4_server41_procedures_names);
477         }
478
479         return (0);
480 }
481
482 static int nfs_submit_nfs4_client (const char *instance, char **fields,
483                 size_t fields_num)
484 {
485         size_t proc40_names_num, proc41_names_num;
486
487         static int suppress_warning = 0;
488
489         switch (fields_num)
490         {
491                 case 34:
492                 case 35:
493                 case 36:
494                 case 37:
495                 case 38:
496                         /* 4.0-only configuration */
497                         proc40_names_num = fields_num;
498                         break;
499                 case 40:
500                 case 41:
501                         proc40_names_num = 35;
502                         break;
503                 case 42:
504                 case 44:
505                         proc40_names_num = 36;
506                         break;
507                 case 46:
508                 case 47:
509                 case 51:
510                 case 53:
511                         proc40_names_num = 37;
512                         break;
513                 case 54:
514                         proc40_names_num = 38;
515                         break;
516                 default:
517                         if (!suppress_warning)
518                         {
519                                 WARNING ("nfs plugin: Unexpected number of "
520                                                 "fields for NFSv4 %s "
521                                                 "statistics: %zu. ",
522                                                 instance, fields_num);
523                         }
524
525                         if (fields_num > 34)
526                         {
527                                 /* safe fallback to basic nfs40 procedures */
528                                 fields_num = 34;
529                                 proc40_names_num = 34;
530
531                                 suppress_warning = 1;
532                         }
533                         else
534                         {
535                                 return (EINVAL);
536                         }
537         }
538
539         nfs_submit_fields (4, instance, fields, proc40_names_num,
540                         nfs4_client40_procedures_names);
541
542         if (fields_num > proc40_names_num)
543         {
544                 proc41_names_num = fields_num - proc40_names_num;
545                 fields += proc40_names_num;
546
547                 nfs_submit_fields (4, instance, fields,proc41_names_num,
548                                  nfs4_client41_procedures_names);
549         }
550
551         return (0);
552 }
553
554 static void nfs_read_linux (FILE *fh, const char *inst)
555 {
556         char buffer[1024];
557
558         char *fields[64];
559         int fields_num = 0;
560
561         if (fh == NULL)
562                 return;
563
564         while (fgets (buffer, sizeof (buffer), fh) != NULL)
565         {
566                 fields_num = strsplit (buffer,
567                                 fields, STATIC_ARRAY_SIZE (fields));
568
569                 if (fields_num < 3)
570                         continue;
571
572                 if (strcmp (fields[0], "proc2") == 0)
573                 {
574                         nfs_submit_fields_safe (/* version = */ 2, inst,
575                                         fields + 2, (size_t) (fields_num - 2),
576                                         nfs2_procedures_names,
577                                         nfs2_procedures_names_num);
578                 }
579                 else if (strncmp (fields[0], "proc3", 5) == 0)
580                 {
581                         nfs_submit_fields_safe (/* version = */ 3, inst,
582                                         fields + 2, (size_t) (fields_num - 2),
583                                         nfs3_procedures_names,
584                                         nfs3_procedures_names_num);
585                 }
586                 else if (strcmp (fields[0], "proc4ops") == 0)
587                 {
588                         if (inst[0] == 's')
589                                 nfs_submit_nfs4_server (inst, fields + 2,
590                                                 (size_t) (fields_num - 2));
591                 }
592                 else if (strcmp (fields[0], "proc4") == 0)
593                 {
594                         if (inst[0] == 'c')
595                                 nfs_submit_nfs4_client (inst, fields + 2,
596                                                 (size_t) (fields_num - 2));
597                 }
598         } /* while (fgets) */
599 } /* void nfs_read_linux */
600 #endif /* KERNEL_LINUX */
601
602 #if HAVE_LIBKSTAT
603 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, const char *inst,
604                 char const **proc_names, size_t proc_names_num)
605 {
606         char plugin_instance[DATA_MAX_NAME_LEN];
607         value_t values[proc_names_num];
608
609         if (ksp == NULL)
610                 return (EINVAL);
611
612         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
613                         nfs_version, inst);
614
615         kstat_read(kc, ksp, NULL);
616         for (size_t i = 0; i < proc_names_num; i++)
617         {
618                 /* The name passed to kstat_data_lookup() doesn't have the
619                  * "const" modifier, so we need to copy the name here. */
620                 char name[32];
621                 sstrncpy (name, proc_names[i], sizeof (name));
622
623                 values[i].counter = (derive_t) get_kstat_value (ksp, name);
624         }
625
626         nfs_procedures_submit (plugin_instance, proc_names, values,
627                         proc_names_num);
628         return (0);
629 }
630 #endif
631
632 #if KERNEL_LINUX
633 static int nfs_read (void)
634 {
635         FILE *fh;
636
637         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
638         {
639                 nfs_read_linux (fh, "client");
640                 fclose (fh);
641         }
642
643         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
644         {
645                 nfs_read_linux (fh, "server");
646                 fclose (fh);
647         }
648
649         return (0);
650 }
651 /* #endif KERNEL_LINUX */
652
653 #elif HAVE_LIBKSTAT
654 static int nfs_read (void)
655 {
656         nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
657                         nfs2_procedures_names, nfs2_procedures_names_num);
658         nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
659                         nfs2_procedures_names, nfs2_procedures_names_num);
660         nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
661                         nfs3_procedures_names, nfs3_procedures_names_num);
662         nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
663                         nfs3_procedures_names, nfs3_procedures_names_num);
664         nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
665                         nfs4_procedures_names, nfs4_procedures_names_num);
666         nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
667                         nfs4_procedures_names, nfs4_procedures_names_num);
668
669         return (0);
670 }
671 #endif /* HAVE_LIBKSTAT */
672
673 void module_register (void)
674 {
675         plugin_register_init ("nfs", nfs_init);
676         plugin_register_read ("nfs", nfs_read);
677 } /* void module_register */