Code

Merge pull request #1832 from rubenk/check-for-c99-compiler
[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         kstat_t *ksp_chain = NULL;
351         nfs2_ksp_client = NULL;
352         nfs2_ksp_server = NULL;
353         nfs3_ksp_client = NULL;
354         nfs3_ksp_server = NULL;
355         nfs4_ksp_client = NULL;
356         nfs4_ksp_server = NULL;
358         if (kc == NULL)
359                 return (-1);
361         for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
362                         ksp_chain = ksp_chain->ks_next)
363         {
364                 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
365                         continue;
366                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
367                         nfs2_ksp_server = ksp_chain;
368                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
369                         nfs3_ksp_server = ksp_chain;
370                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
371                         nfs4_ksp_server = ksp_chain;
372                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
373                         nfs2_ksp_client = ksp_chain;
374                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
375                         nfs3_ksp_client = ksp_chain;
376                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
377                         nfs4_ksp_client = ksp_chain;
378         }
380         return (0);
381 } /* int nfs_init */
382 #endif
384 static void nfs_procedures_submit (const char *plugin_instance,
385                 const char **type_instances,
386                 value_t *values, size_t values_num)
388         value_list_t vl = VALUE_LIST_INIT;
389         size_t i;
391         vl.values_len = 1;
392         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
393         sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
394         sstrncpy (vl.plugin_instance, plugin_instance,
395                         sizeof (vl.plugin_instance));
396         sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
398         for (i = 0; i < values_num; i++)
399         {
400                 vl.values = values + i;
401                 sstrncpy (vl.type_instance, type_instances[i],
402                                 sizeof (vl.type_instance));
403                 plugin_dispatch_values (&vl);
404         }
405 } /* void nfs_procedures_submit */
407 #if KERNEL_LINUX
408 static void nfs_submit_fields (int nfs_version, const char *instance,
409                 char **fields, size_t fields_num, const char **proc_names)
411         char plugin_instance[DATA_MAX_NAME_LEN];
412         value_t values[fields_num];
413         size_t i;
415         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
416                         nfs_version, instance);
418         for (i = 0; i < fields_num; i++)
419                 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
421         nfs_procedures_submit (plugin_instance, proc_names, values,
422                         fields_num);
425 static int nfs_submit_fields_safe (int nfs_version, const char *instance,
426                 char **fields, size_t fields_num,
427                 const char **proc_names, size_t proc_names_num)
429         if (fields_num != proc_names_num)
430         {
431                 WARNING ("nfs plugin: Wrong number of fields for "
432                                 "NFSv%i %s statistics. Expected %zu, got %zu.",
433                                 nfs_version, instance,
434                                 proc_names_num, fields_num);
435                 return (EINVAL);
436         }
438         nfs_submit_fields (nfs_version, instance, fields, fields_num,
439                         proc_names);
441         return (0);
444 static int nfs_submit_nfs4_server (const char *instance, char **fields,
445                 size_t fields_num)
447         static int suppress_warning = 0;
449         if (fields_num != NFS4_SERVER40_NUM_PROC &&
450                 fields_num != NFS4_SERVER41_NUM_PROC)
451         {
452                 if (!suppress_warning)
453                 {
454                         WARNING ("nfs plugin: Unexpected number of fields for "
455                                         "NFSv4 %s statistics: %zu. ",
456                                         instance, fields_num);
457                 }
459                 if (fields_num > NFS4_SERVER_MAX_PROC)
460                 {
461                         fields_num = NFS4_SERVER_MAX_PROC;
462                         suppress_warning = 1;
463                 }
464                 else
465                 {
466                         return (EINVAL);
467                 }
468         }
470         nfs_submit_fields (4, instance, fields,
471                         nfs4_server40_procedures_names_num,
472                         nfs4_server40_procedures_names);
474         if (fields_num >= NFS4_SERVER41_NUM_PROC)
475         {
476                 fields += nfs4_server40_procedures_names_num;
478                 nfs_submit_fields (4, instance, fields,
479                                 nfs4_server41_procedures_names_num,
480                                 nfs4_server41_procedures_names);
481         }
483         return (0);
486 static int nfs_submit_nfs4_client (const char *instance, char **fields,
487                 size_t fields_num)
489         size_t proc40_names_num, proc41_names_num;
491         static int suppress_warning = 0;
493         switch (fields_num)
494         {
495                 case 34:
496                 case 35:
497                 case 36:
498                 case 37:
499                 case 38:
500                         /* 4.0-only configuration */
501                         proc40_names_num = fields_num;
502                         break;
503                 case 40:
504                 case 41:
505                         proc40_names_num = 35;
506                         break;
507                 case 42:
508                 case 44:
509                         proc40_names_num = 36;
510                         break;
511                 case 46:
512                 case 47:
513                 case 51:
514                 case 53:
515                         proc40_names_num = 37;
516                         break;
517                 case 54:
518                         proc40_names_num = 38;
519                         break;
520                 default:
521                         if (!suppress_warning)
522                         {
523                                 WARNING ("nfs plugin: Unexpected number of "
524                                                 "fields for NFSv4 %s "
525                                                 "statistics: %zu. ",
526                                                 instance, fields_num);
527                         }
529                         if (fields_num > 34)
530                         {
531                                 /* safe fallback to basic nfs40 procedures */
532                                 fields_num = 34;
533                                 proc40_names_num = 34;
535                                 suppress_warning = 1;
536                         }
537                         else
538                         {
539                                 return (EINVAL);
540                         }
541         }
543         nfs_submit_fields (4, instance, fields, proc40_names_num,
544                         nfs4_client40_procedures_names);
546         if (fields_num > proc40_names_num)
547         {
548                 proc41_names_num = fields_num - proc40_names_num;
549                 fields += proc40_names_num;
551                 nfs_submit_fields (4, instance, fields,proc41_names_num,
552                                  nfs4_client41_procedures_names);
553         }
555         return (0);
558 static void nfs_read_linux (FILE *fh, const char *inst)
560         char buffer[1024];
562         char *fields[64];
563         int fields_num = 0;
565         if (fh == NULL)
566                 return;
568         while (fgets (buffer, sizeof (buffer), fh) != NULL)
569         {
570                 fields_num = strsplit (buffer,
571                                 fields, STATIC_ARRAY_SIZE (fields));
573                 if (fields_num < 3)
574                         continue;
576                 if (strcmp (fields[0], "proc2") == 0)
577                 {
578                         nfs_submit_fields_safe (/* version = */ 2, inst,
579                                         fields + 2, (size_t) (fields_num - 2),
580                                         nfs2_procedures_names,
581                                         nfs2_procedures_names_num);
582                 }
583                 else if (strncmp (fields[0], "proc3", 5) == 0)
584                 {
585                         nfs_submit_fields_safe (/* version = */ 3, inst,
586                                         fields + 2, (size_t) (fields_num - 2),
587                                         nfs3_procedures_names,
588                                         nfs3_procedures_names_num);
589                 }
590                 else if (strcmp (fields[0], "proc4ops") == 0)
591                 {
592                         if (inst[0] == 's')
593                                 nfs_submit_nfs4_server (inst, fields + 2,
594                                                 (size_t) (fields_num - 2));
595                 }
596                 else if (strcmp (fields[0], "proc4") == 0)
597                 {
598                         if (inst[0] == 'c')
599                                 nfs_submit_nfs4_client (inst, fields + 2,
600                                                 (size_t) (fields_num - 2));
601                 }
602         } /* while (fgets) */
603 } /* void nfs_read_linux */
604 #endif /* KERNEL_LINUX */
606 #if HAVE_LIBKSTAT
607 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, const char *inst,
608                 char const **proc_names, size_t proc_names_num)
610         char plugin_instance[DATA_MAX_NAME_LEN];
611         value_t values[proc_names_num];
612         size_t i;
614         if (ksp == NULL)
615                 return (EINVAL);
617         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
618                         nfs_version, inst);
620         kstat_read(kc, ksp, NULL);
621         for (i = 0; i < proc_names_num; i++)
622         {
623                 /* The name passed to kstat_data_lookup() doesn't have the
624                  * "const" modifier, so we need to copy the name here. */
625                 char name[32];
626                 sstrncpy (name, proc_names[i], sizeof (name));
628                 values[i].counter = (derive_t) get_kstat_value (ksp, name);
629         }
631         nfs_procedures_submit (plugin_instance, proc_names, values,
632                         proc_names_num);
633         return (0);
635 #endif
637 #if KERNEL_LINUX
638 static int nfs_read (void)
640         FILE *fh;
642         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
643         {
644                 nfs_read_linux (fh, "client");
645                 fclose (fh);
646         }
648         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
649         {
650                 nfs_read_linux (fh, "server");
651                 fclose (fh);
652         }
654         return (0);
656 /* #endif KERNEL_LINUX */
658 #elif HAVE_LIBKSTAT
659 static int nfs_read (void)
661         nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
662                         nfs2_procedures_names, nfs2_procedures_names_num);
663         nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
664                         nfs2_procedures_names, nfs2_procedures_names_num);
665         nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
666                         nfs3_procedures_names, nfs3_procedures_names_num);
667         nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
668                         nfs3_procedures_names, nfs3_procedures_names_num);
669         nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
670                         nfs4_procedures_names, nfs4_procedures_names_num);
671         nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
672                         nfs4_procedures_names, nfs4_procedures_names_num);
674         return (0);
676 #endif /* HAVE_LIBKSTAT */
678 void module_register (void)
680         plugin_register_init ("nfs", nfs_init);
681         plugin_register_read ("nfs", nfs_read);
682 } /* void module_register */