Code

Merge remote-tracking branch 'github/pr/2012' into collectd-5.6
[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  **/
25 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
30 #if HAVE_KSTAT_H
31 #include <kstat.h>
32 #endif
34 /*
35 see /proc/net/rpc/nfs
36 see http://www.missioncriticallinux.com/orph/NFS-Statistics
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.
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.
49 proc2 x x x...
50 proc3 x x x...
52 Procedure   NFS Version NFS Version 3
53 Number      Procedures  Procedures
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 */
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);
102 static const char *nfs3_procedures_names[] =
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);
129 #if HAVE_LIBKSTAT
130 static const char *nfs4_procedures_names[] =
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
175 #if KERNEL_LINUX
176 static const char *nfs4_server40_procedures_names[] =
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 };
220 static size_t nfs4_server40_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server40_procedures_names);
222 static const char *nfs4_server41_procedures_names[] =
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 };
245 static size_t nfs4_server41_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server41_procedures_names);
247 #define NFS4_SERVER40_NUM_PROC ( \
248         STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) )
250 #define NFS4_SERVER41_NUM_PROC ( \
251         STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) + \
252         STATIC_ARRAY_SIZE (nfs4_server41_procedures_names) )
254 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
256 static const char *nfs4_client40_procedures_names[] =
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 };
298 static const char *nfs4_client41_procedures_names[] =
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 };
318 #define NFS4_CLIENT40_NUM_PROC ( \
319         STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) )
321 #define NFS4_CLIENT41_NUM_PROC ( \
322         STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) + \
323         STATIC_ARRAY_SIZE (nfs4_client41_procedures_names) )
325 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
327 #endif
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
339 #if KERNEL_LINUX
340 static int nfs_init (void)
342         return (0);
344 /* #endif KERNEL_LINUX */
346 #elif HAVE_LIBKSTAT
347 static int nfs_init (void)
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;
356         if (kc == NULL)
357                 return (-1);
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         }
378         return (0);
379 } /* int nfs_init */
380 #endif
382 static void nfs_procedures_submit (const char *plugin_instance,
383                 const char **type_instances,
384                 value_t *values, size_t values_num)
386         value_list_t vl = VALUE_LIST_INIT;
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));
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 */
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)
408         char plugin_instance[DATA_MAX_NAME_LEN];
409         value_t values[fields_num];
411         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
412                         nfs_version, instance);
414         for (size_t i = 0; i < fields_num; i++)
415                 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
417         nfs_procedures_submit (plugin_instance, proc_names, values,
418                         fields_num);
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)
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         }
434         nfs_submit_fields (nfs_version, instance, fields, fields_num,
435                         proc_names);
437         return (0);
440 static int nfs_submit_nfs4_server (const char *instance, char **fields,
441                 size_t fields_num)
443         static int suppress_warning = 0;
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                 }
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         }
466         nfs_submit_fields (4, instance, fields,
467                         nfs4_server40_procedures_names_num,
468                         nfs4_server40_procedures_names);
470         if (fields_num >= NFS4_SERVER41_NUM_PROC)
471         {
472                 fields += nfs4_server40_procedures_names_num;
474                 nfs_submit_fields (4, instance, fields,
475                                 nfs4_server41_procedures_names_num,
476                                 nfs4_server41_procedures_names);
477         }
479         return (0);
482 static int nfs_submit_nfs4_client (const char *instance, char **fields,
483                 size_t fields_num)
485         size_t proc40_names_num, proc41_names_num;
487         static int suppress_warning = 0;
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                         }
525                         if (fields_num > 34)
526                         {
527                                 /* safe fallback to basic nfs40 procedures */
528                                 fields_num = 34;
529                                 proc40_names_num = 34;
531                                 suppress_warning = 1;
532                         }
533                         else
534                         {
535                                 return (EINVAL);
536                         }
537         }
539         nfs_submit_fields (4, instance, fields, proc40_names_num,
540                         nfs4_client40_procedures_names);
542         if (fields_num > proc40_names_num)
543         {
544                 proc41_names_num = fields_num - proc40_names_num;
545                 fields += proc40_names_num;
547                 nfs_submit_fields (4, instance, fields,proc41_names_num,
548                                  nfs4_client41_procedures_names);
549         }
551         return (0);
554 static void nfs_read_linux (FILE *fh, const char *inst)
556         char buffer[1024];
558         char *fields[64];
559         int fields_num = 0;
561         if (fh == NULL)
562                 return;
564         while (fgets (buffer, sizeof (buffer), fh) != NULL)
565         {
566                 fields_num = strsplit (buffer,
567                                 fields, STATIC_ARRAY_SIZE (fields));
569                 if (fields_num < 3)
570                         continue;
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 */
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)
606         char plugin_instance[DATA_MAX_NAME_LEN];
607         value_t values[proc_names_num];
609         if (ksp == NULL)
610                 return (EINVAL);
612         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
613                         nfs_version, inst);
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));
623                 values[i].counter = (derive_t) get_kstat_value (ksp, name);
624         }
626         nfs_procedures_submit (plugin_instance, proc_names, values,
627                         proc_names_num);
628         return (0);
630 #endif
632 #if KERNEL_LINUX
633 static int nfs_read (void)
635         FILE *fh;
637         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
638         {
639                 nfs_read_linux (fh, "client");
640                 fclose (fh);
641         }
643         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
644         {
645                 nfs_read_linux (fh, "server");
646                 fclose (fh);
647         }
649         return (0);
651 /* #endif KERNEL_LINUX */
653 #elif HAVE_LIBKSTAT
654 static int nfs_read (void)
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);
669         return (0);
671 #endif /* HAVE_LIBKSTAT */
673 void module_register (void)
675         plugin_register_init ("nfs", nfs_init);
676         plugin_register_read ("nfs", nfs_read);
677 } /* void module_register */