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