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                 case 46:
510                 case 47:
511                 case 51:
512                 case 53:
513                         proc40_names_num = 37;
514                         break;
515                 case 54:
516                         proc40_names_num = 38;
517                         break;
518                 default:
519                         if (!suppress_warning)
520                         {
521                                 WARNING ("nfs plugin: Unexpected number of "
522                                                 "fields for NFSv4 %s "
523                                                 "statistics: %zu. ",
524                                                 instance, fields_num);
525                         }
526
527                         if (fields_num > 34)
528                         {
529                                 /* safe fallback to basic nfs40 procedures */
530                                 fields_num = 34;
531                                 proc40_names_num = 34;
532
533                                 suppress_warning = 1;
534                         }
535                         else
536                         {
537                                 return (EINVAL);
538                         }
539         }
540
541         nfs_submit_fields (4, instance, fields, proc40_names_num,
542                         nfs4_client40_procedures_names);
543
544         if (fields_num > proc40_names_num)
545         {
546                 proc41_names_num = fields_num - proc40_names_num;
547                 fields += proc40_names_num;
548
549                 nfs_submit_fields (4, instance, fields,proc41_names_num,
550                                  nfs4_client41_procedures_names);
551         }
552
553         return (0);
554 }
555
556 static void nfs_read_linux (FILE *fh, char *inst)
557 {
558         char buffer[1024];
559
560         char *fields[64];
561         int fields_num = 0;
562
563         if (fh == NULL)
564                 return;
565
566         while (fgets (buffer, sizeof (buffer), fh) != NULL)
567         {
568                 fields_num = strsplit (buffer,
569                                 fields, STATIC_ARRAY_SIZE (fields));
570
571                 if (fields_num < 3)
572                         continue;
573
574                 if (strcmp (fields[0], "proc2") == 0)
575                 {
576                         nfs_submit_fields_safe (/* version = */ 2, inst,
577                                         fields + 2, (size_t) (fields_num - 2),
578                                         nfs2_procedures_names,
579                                         nfs2_procedures_names_num);
580                 }
581                 else if (strncmp (fields[0], "proc3", 5) == 0)
582                 {
583                         nfs_submit_fields_safe (/* version = */ 3, inst,
584                                         fields + 2, (size_t) (fields_num - 2),
585                                         nfs3_procedures_names,
586                                         nfs3_procedures_names_num);
587                 }
588                 else if (strcmp (fields[0], "proc4ops") == 0)
589                 {
590                         if (inst[0] == 's')
591                                 nfs_submit_nfs4_server (inst, fields + 2, 
592                                                 (size_t) (fields_num - 2));
593                 }
594                 else if (strcmp (fields[0], "proc4") == 0)
595                 {
596                         if (inst[0] == 'c')
597                                 nfs_submit_nfs4_client (inst, fields + 2,
598                                                 (size_t) (fields_num - 2));                     
599                 }
600         } /* while (fgets) */
601 } /* void nfs_read_linux */
602 #endif /* KERNEL_LINUX */
603
604 #if HAVE_LIBKSTAT
605 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *inst,
606                 char const **proc_names, size_t proc_names_num)
607 {
608         char plugin_instance[DATA_MAX_NAME_LEN];
609         value_t values[proc_names_num];
610         size_t i;
611
612         if (ksp == NULL)
613                 return (EINVAL);
614
615         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
616                         nfs_version, inst);
617
618         kstat_read(kc, ksp, NULL);
619         for (i = 0; i < proc_names_num; i++)
620         {
621                 /* The name passed to kstat_data_lookup() doesn't have the
622                  * "const" modifier, so we need to copy the name here. */
623                 char name[32];
624                 sstrncpy (name, proc_names[i], sizeof (name));
625
626                 values[i].counter = (derive_t) get_kstat_value (ksp, name);
627         }
628
629         nfs_procedures_submit (plugin_instance, proc_names, values,
630                         proc_names_num);
631         return (0);
632 }
633 #endif
634
635 #if KERNEL_LINUX
636 static int nfs_read (void)
637 {
638         FILE *fh;
639
640         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
641         {
642                 nfs_read_linux (fh, "client");
643                 fclose (fh);
644         }
645
646         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
647         {
648                 nfs_read_linux (fh, "server");
649                 fclose (fh);
650         }
651
652         return (0);
653 }
654 /* #endif KERNEL_LINUX */
655
656 #elif HAVE_LIBKSTAT
657 static int nfs_read (void)
658 {
659         nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
660                         nfs2_procedures_names, nfs2_procedures_names_num);
661         nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
662                         nfs2_procedures_names, nfs2_procedures_names_num);
663         nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
664                         nfs3_procedures_names, nfs3_procedures_names_num);
665         nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
666                         nfs3_procedures_names, nfs3_procedures_names_num);
667         nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
668                         nfs4_procedures_names, nfs4_procedures_names_num);
669         nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
670                         nfs4_procedures_names, nfs4_procedures_names_num);
671
672         return (0);
673 }
674 #endif /* HAVE_LIBKSTAT */
675
676 void module_register (void)
677 {
678         plugin_register_init ("nfs", nfs_init);
679         plugin_register_read ("nfs", nfs_read);
680 } /* void module_register */