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  **/
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         kstat_t *ksp_chain = NULL;
350
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;
357
358         if (kc == NULL)
359                 return (-1);
360
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         }
379
380         return (0);
381 } /* int nfs_init */
382 #endif
383
384 static void nfs_procedures_submit (const char *plugin_instance,
385                 const char **type_instances,
386                 value_t *values, size_t values_num)
387 {
388         value_list_t vl = VALUE_LIST_INIT;
389         size_t i;
390
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));
397
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 */
406
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)
410 {
411         char plugin_instance[DATA_MAX_NAME_LEN];
412         value_t values[fields_num];
413         size_t i;
414
415         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
416                         nfs_version, instance);
417
418         for (i = 0; i < fields_num; i++)
419                 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
420
421         nfs_procedures_submit (plugin_instance, proc_names, values,
422                         fields_num);
423 }
424
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)
428 {
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         }
437
438         nfs_submit_fields (nfs_version, instance, fields, fields_num,
439                         proc_names);
440
441         return (0);
442 }
443
444 static int nfs_submit_nfs4_server (const char *instance, char **fields,
445                 size_t fields_num)
446 {
447         static int suppress_warning = 0;
448
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                 }
458
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         }
469
470         nfs_submit_fields (4, instance, fields,
471                         nfs4_server40_procedures_names_num,
472                         nfs4_server40_procedures_names);
473
474         if (fields_num >= NFS4_SERVER41_NUM_PROC)
475         {
476                 fields += nfs4_server40_procedures_names_num;
477
478                 nfs_submit_fields (4, instance, fields,
479                                 nfs4_server41_procedures_names_num,
480                                 nfs4_server41_procedures_names);
481         }
482
483         return (0);
484 }
485
486 static int nfs_submit_nfs4_client (const char *instance, char **fields,
487                 size_t fields_num)
488 {
489         size_t proc40_names_num, proc41_names_num;
490
491         static int suppress_warning = 0;
492
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                         }
528
529                         if (fields_num > 34)
530                         {
531                                 /* safe fallback to basic nfs40 procedures */
532                                 fields_num = 34;
533                                 proc40_names_num = 34;
534
535                                 suppress_warning = 1;
536                         }
537                         else
538                         {
539                                 return (EINVAL);
540                         }
541         }
542
543         nfs_submit_fields (4, instance, fields, proc40_names_num,
544                         nfs4_client40_procedures_names);
545
546         if (fields_num > proc40_names_num)
547         {
548                 proc41_names_num = fields_num - proc40_names_num;
549                 fields += proc40_names_num;
550
551                 nfs_submit_fields (4, instance, fields,proc41_names_num,
552                                  nfs4_client41_procedures_names);
553         }
554
555         return (0);
556 }
557
558 static void nfs_read_linux (FILE *fh, const char *inst)
559 {
560         char buffer[1024];
561
562         char *fields[64];
563         int fields_num = 0;
564
565         if (fh == NULL)
566                 return;
567
568         while (fgets (buffer, sizeof (buffer), fh) != NULL)
569         {
570                 fields_num = strsplit (buffer,
571                                 fields, STATIC_ARRAY_SIZE (fields));
572
573                 if (fields_num < 3)
574                         continue;
575
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 */
605
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)
609 {
610         char plugin_instance[DATA_MAX_NAME_LEN];
611         value_t values[proc_names_num];
612         size_t i;
613
614         if (ksp == NULL)
615                 return (EINVAL);
616
617         ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
618                         nfs_version, inst);
619
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));
627
628                 values[i].counter = (derive_t) get_kstat_value (ksp, name);
629         }
630
631         nfs_procedures_submit (plugin_instance, proc_names, values,
632                         proc_names_num);
633         return (0);
634 }
635 #endif
636
637 #if KERNEL_LINUX
638 static int nfs_read (void)
639 {
640         FILE *fh;
641
642         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
643         {
644                 nfs_read_linux (fh, "client");
645                 fclose (fh);
646         }
647
648         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
649         {
650                 nfs_read_linux (fh, "server");
651                 fclose (fh);
652         }
653
654         return (0);
655 }
656 /* #endif KERNEL_LINUX */
657
658 #elif HAVE_LIBKSTAT
659 static int nfs_read (void)
660 {
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);
673
674         return (0);
675 }
676 #endif /* HAVE_LIBKSTAT */
677
678 void module_register (void)
679 {
680         plugin_register_init ("nfs", nfs_init);
681         plugin_register_read ("nfs", nfs_read);
682 } /* void module_register */