f585b974d227cbedc006446e32c584b07509e169
[collectd.git] / src / nfs.c
1 /**
2  * collectd - src/nfs.c
3  * Copyright (C) 2005,2006  Jason Pepas
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Jason Pepas <cell at ices.utexas.edu>
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "utils_debug.h"
27
28 #define MODULE_NAME "nfs"
29
30 /* #if defined(KERNEL_LINUX) || defined(HAVE_LIBKSTAT) */
31 #if KERNEL_LINUX
32 # define NFS_HAVE_READ 1
33 #else
34 # define NFS_HAVE_READ 0
35 #endif
36
37 /*
38 see /proc/net/rpc/nfs
39 see http://www.missioncriticallinux.com/orph/NFS-Statistics
40
41 net x x x x
42 rpc_stat.netcnt         Not used; always zero.
43 rpc_stat.netudpcnt      Not used; always zero.
44 rpc_stat.nettcpcnt      Not used; always zero.
45 rpc_stat.nettcpconn     Not used; always zero.
46
47 rpc x x x
48 rpc_stat.rpccnt             The number of RPC calls.
49 rpc_stat.rpcretrans         The number of retransmitted RPC calls.
50 rpc_stat.rpcauthrefresh     The number of credential refreshes.
51
52 proc2 x x x...
53 proc3 x x x...
54
55 Procedure   NFS Version NFS Version 3
56 Number      Procedures  Procedures
57
58 0           null        null
59 1           getattr     getattr
60 2           setattr     setattr
61 3           root        lookup
62 4           lookup      access
63 5           readlink    readlink
64 6           read        read
65 7           wrcache     write
66 8           write       create
67 9           create      mkdir
68 10          remove      symlink
69 11          rename      mknod
70 12          link        remove
71 13          symlink     rmdir
72 14          mkdir       rename
73 15          rmdir       link
74 16          readdir     readdir
75 17          fsstat      readdirplus
76 18                      fsstat
77 19                      fsinfo
78 20                      pathconf
79 21                      commit
80 */
81
82 static data_source_t procedure_dsrc[1] =
83 {
84         {"value", DS_TYPE_COUNTER, 0, 4294967295.0}
85 };
86
87 static data_set_t procedure_ds =
88 {
89         "nfs_procedure", 1, procedure_dsrc
90 };
91
92 static const char *nfs2_procedures_names[] =
93 {
94         "null",
95         "getattr",
96         "setattr",
97         "root",
98         "lookup",
99         "readlink",
100         "read",
101         "wrcache",
102         "write",
103         "create",
104         "remove",
105         "rename",
106         "link",
107         "symlink",
108         "mkdir",
109         "rmdir",
110         "readdir",
111         "fsstat",
112         NULL
113 };
114 static int nfs2_procedures_names_num = 18;
115
116 static const char *nfs3_procedures_names[] =
117 {
118         "null",
119         "getattr",
120         "setattr",
121         "lookup",
122         "access",
123         "readlink",
124         "read",
125         "write",
126         "create",
127         "mkdir",
128         "symlink",
129         "mknod",
130         "remove",
131         "rmdir",
132         "rename",
133         "link",
134         "readdir",
135         "readdirplus",
136         "fsstat",
137         "fsinfo",
138         "pathconf",
139         "commit",
140         NULL
141 };
142 static int nfs3_procedures_names_num = 22;
143
144 #if HAVE_LIBKSTAT && 0
145 extern kstat_ctl_t *kc;
146 static kstat_t *nfs2_ksp_client;
147 static kstat_t *nfs2_ksp_server;
148 static kstat_t *nfs3_ksp_client;
149 static kstat_t *nfs3_ksp_server;
150 static kstat_t *nfs4_ksp_client;
151 static kstat_t *nfs4_ksp_server;
152 #endif
153
154 /* Possibly TODO: NFSv4 statistics */
155
156 #if 0
157 static int nfs_init (void)
158 {
159 #if HAVE_LIBKSTAT && 0
160         kstat_t *ksp_chain;
161
162         nfs2_ksp_client = NULL;
163         nfs2_ksp_server = NULL;
164         nfs3_ksp_client = NULL;
165         nfs3_ksp_server = NULL;
166         nfs4_ksp_client = NULL;
167         nfs4_ksp_server = NULL;
168         
169         if (kc == NULL)
170                 return;
171
172         for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
173                         ksp_chain = ksp_chain->ks_next)
174         {
175                 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
176                         continue;
177                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
178                         nfs2_ksp_server = ksp_chain;
179                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
180                         nfs3_ksp_server = ksp_chain;
181                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
182                         nfs4_ksp_server = ksp_chain;
183                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
184                         nfs2_ksp_client = ksp_chain;
185                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
186                         nfs3_ksp_client = ksp_chain;
187                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
188                         nfs4_ksp_client = ksp_chain;
189         }
190 #endif
191
192         return (0);
193 } /* int nfs_init */
194 #endif
195
196 #define BUFSIZE 1024
197 #if NFS_HAVE_READ
198 static void nfs_procedures_submit (const char *plugin_instance,
199                 unsigned long long *val, const char **names, int len)
200 {
201         value_t values[1];
202         value_list_t vl = VALUE_LIST_INIT;
203         int i;
204
205         vl.values = values;
206         vl.values_len = 1;
207         vl.time = time (NULL);
208         strcpy (vl.host, hostname);
209         strcpy (vl.plugin, "nfs");
210         strncpy (vl.plugin_instance, plugin_instance,
211                         sizeof (vl.plugin_instance));
212
213         for (i = 0; i < len; i++)
214         {
215                 values[0].counter = val[i];
216                 strncpy (vl.type_instance, names[i],
217                                 sizeof (vl.type_instance));
218                 DBG ("%s-%s/nfs_procedure-%s = %llu",
219                                 vl.plugin, vl.plugin_instance,
220                                 vl.type_instance, val[i]);
221                 plugin_dispatch_values ("nfs_procedure", &vl);
222         }
223 } /* void nfs_procedures_submit */
224 #endif /* NFS_HAVE_READ */
225
226 #if KERNEL_LINUX
227 static void nfs_read_stats_file (FILE *fh, char *inst)
228 {
229         char buffer[BUFSIZE];
230
231         char plugin_instance[DATA_MAX_NAME_LEN];
232
233         char *fields[48];
234         int numfields = 0;
235
236         if (fh == NULL)
237                 return;
238
239         while (fgets (buffer, BUFSIZE, fh) != NULL)
240         {
241                 numfields = strsplit (buffer, fields, 48);
242
243                 if (((numfields - 2) != nfs2_procedures_names_num)
244                                 && ((numfields - 2)
245                                         != nfs3_procedures_names_num))
246                         continue;
247
248                 if (strcmp (fields[0], "proc2") == 0)
249                 {
250                         int i;
251                         unsigned long long *values;
252
253                         if ((numfields - 2) != nfs2_procedures_names_num)
254                         {
255                                 syslog (LOG_WARNING, "nfs plugin: Wrong "
256                                                 "number of fields (= %i) "
257                                                 "for NFSv2 statistics.",
258                                                 numfields - 2);
259                                 continue;
260                         }
261
262                         snprintf (plugin_instance, sizeof (plugin_instance),
263                                         "v2%s", inst);
264                         plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
265
266                         values = (unsigned long long *) malloc (nfs2_procedures_names_num * sizeof (unsigned long long));
267                         if (values == NULL)
268                         {
269                                 syslog (LOG_ERR, "nfs plugin: malloc "
270                                                 "failed: %s",
271                                                 strerror (errno));
272                                 continue;
273                         }
274
275                         for (i = 0; i < nfs2_procedures_names_num; i++)
276                                 values[i] = atoll (fields[i + 2]);
277
278                         nfs_procedures_submit (plugin_instance, values,
279                                         nfs2_procedures_names,
280                                         nfs2_procedures_names_num);
281
282                         free (values);
283                 }
284                 else if (strncmp (fields[0], "proc3", 5) == 0)
285                 {
286                         int i;
287                         unsigned long long *values;
288
289                         if ((numfields - 2) != nfs3_procedures_names_num)
290                         {
291                                 syslog (LOG_WARNING, "nfs plugin: Wrong "
292                                                 "number of fields (= %i) "
293                                                 "for NFSv3 statistics.",
294                                                 numfields - 2);
295                                 continue;
296                         }
297
298                         snprintf (plugin_instance, sizeof (plugin_instance),
299                                         "v3%s", inst);
300                         plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
301
302                         values = (unsigned long long *) malloc (nfs3_procedures_names_num * sizeof (unsigned long long));
303                         if (values == NULL)
304                         {
305                                 syslog (LOG_ERR, "nfs plugin: malloc "
306                                                 "failed: %s",
307                                                 strerror (errno));
308                                 continue;
309                         }
310
311                         for (i = 0; i < nfs3_procedures_names_num; i++)
312                                 values[i] = atoll (fields[i + 2]);
313
314                         nfs_procedures_submit (plugin_instance, values,
315                                         nfs3_procedures_names,
316                                         nfs3_procedures_names_num);
317
318                         free (values);
319                 }
320         } /* while (fgets (buffer, BUFSIZE, fh) != NULL) */
321 } /* void nfs_read_stats_file */
322 #endif /* defined(KERNEL_LINUX) */
323 #undef BUFSIZE
324
325 #if HAVE_LIBKSTAT && 0
326 static void nfs2_read_kstat (kstat_t *ksp, char *inst)
327 {
328         unsigned long long values[18];
329
330         values[0] = get_kstat_value (ksp, "null");
331         values[1] = get_kstat_value (ksp, "getattr");
332         values[2] = get_kstat_value (ksp, "setattr");
333         values[3] = get_kstat_value (ksp, "root");
334         values[4] = get_kstat_value (ksp, "lookup");
335         values[5] = get_kstat_value (ksp, "readlink");
336         values[6] = get_kstat_value (ksp, "read");
337         values[7] = get_kstat_value (ksp, "wrcache");
338         values[8] = get_kstat_value (ksp, "write");
339         values[9] = get_kstat_value (ksp, "create");
340         values[10] = get_kstat_value (ksp, "remove");
341         values[11] = get_kstat_value (ksp, "rename");
342         values[12] = get_kstat_value (ksp, "link");
343         values[13] = get_kstat_value (ksp, "symlink");
344         values[14] = get_kstat_value (ksp, "mkdir");
345         values[15] = get_kstat_value (ksp, "rmdir");
346         values[16] = get_kstat_value (ksp, "readdir");
347         values[17] = get_kstat_value (ksp, "statfs");
348
349         nfs2_procedures_submit (values, inst);
350 }
351 #endif
352
353 #if NFS_HAVE_READ
354 static int nfs_read (void)
355 {
356 #if KERNEL_LINUX
357         FILE *fh;
358
359         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
360         {
361                 nfs_read_stats_file (fh, "client");
362                 fclose (fh);
363         }
364
365         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
366         {
367                 nfs_read_stats_file (fh, "server");
368                 fclose (fh);
369         }
370
371 /* #endif defined(KERNEL_LINUX) */
372
373 #elif HAVE_LIBKSTAT && 0
374         if (nfs2_ksp_client != NULL)
375                 nfs2_read_kstat (nfs2_ksp_client, "client");
376         if (nfs2_ksp_server != NULL)
377                 nfs2_read_kstat (nfs2_ksp_server, "server");
378 #endif /* defined(HAVE_LIBKSTAT) */
379
380         return (0);
381 }
382 #endif /* NFS_HAVE_READ */
383
384 void module_register (void)
385 {
386         plugin_register_data_set (&procedure_ds);
387
388 #if NFS_HAVE_READ
389         plugin_register_read ("nfs", nfs_read);
390 #endif
391 }
392
393 #undef MODULE_NAME