Added copyright/GPL header to all .c and .h files in trunk
[collectd.git] / src / nfs.c
1 /**
2  * collectd - src/nfs.c
3  * Copyright (C) 2005  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; either version 2 of the License, or (at your
8  * option) any later version.
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 verplant.org>
22  **/
23
24 #include "nfs.h"
25
26 #if COLLECT_NFS
27 #define MODULE_NAME "nfs"
28
29 #include "plugin.h"
30 #include "common.h"
31
32 static char *nfs2_procedures_file  = "nfs2_procedures-%s.rrd";
33 static char *nfs3_procedures_file  = "nfs3_procedures-%s.rrd";
34
35 /*
36 see /proc/net/rpc/nfs
37 see http://www.missioncriticallinux.com/orph/NFS-Statistics
38
39 net x x x x
40 rpc_stat.netcnt         Not used; always zero.
41 rpc_stat.netudpcnt      Not used; always zero.
42 rpc_stat.nettcpcnt      Not used; always zero.
43 rpc_stat.nettcpconn     Not used; always zero.
44
45 rpc x x x
46 rpc_stat.rpccnt             The number of RPC calls.
47 rpc_stat.rpcretrans         The number of retransmitted RPC calls.
48 rpc_stat.rpcauthrefresh     The number of credential refreshes.
49
50 proc2 x x x...
51 proc3 x x x...
52
53 Procedure   NFS Version NFS Version 3
54 Number      Procedures  Procedures
55
56 0           null        null
57 1           getattr     getattr
58 2           setattr     setattr
59 3           root        lookup
60 4           lookup      access
61 5           readlink    readlink
62 6           read        read
63 7           wrcache     write
64 8           write       create
65 9           create      mkdir
66 10          remove      symlink
67 11          rename      mknod
68 12          link        remove
69 13          symlink     rmdir
70 14          mkdir       rename
71 15          rmdir       link
72 16          readdir     readdir
73 17          fsstat      readdirplus
74 18                      fsstat
75 19                      fsinfo
76 20                      pathconf
77 21                      commit
78 */
79
80 static char *nfs2_procedures_ds_def[] =
81 {
82         "DS:null:COUNTER:25:0:U",
83         "DS:getattr:COUNTER:25:0:U",
84         "DS:setattr:COUNTER:25:0:U",
85         "DS:root:COUNTER:25:0:U",
86         "DS:lookup:COUNTER:25:0:U",
87         "DS:readlink:COUNTER:25:0:U",
88         "DS:read:COUNTER:25:0:U",
89         "DS:wrcache:COUNTER:25:0:U",
90         "DS:write:COUNTER:25:0:U",
91         "DS:create:COUNTER:25:0:U",
92         "DS:remove:COUNTER:25:0:U",
93         "DS:rename:COUNTER:25:0:U",
94         "DS:link:COUNTER:25:0:U",
95         "DS:symlink:COUNTER:25:0:U",
96         "DS:mkdir:COUNTER:25:0:U",
97         "DS:rmdir:COUNTER:25:0:U",
98         "DS:readdir:COUNTER:25:0:U",
99         "DS:fsstat:COUNTER:25:0:U",
100         NULL
101 };
102 static int nfs2_procedures_ds_num = 18;
103
104 static char *nfs3_procedures_ds_def[] =
105 {
106         "DS:null:COUNTER:25:0:U",
107         "DS:getattr:COUNTER:25:0:U",
108         "DS:setattr:COUNTER:25:0:U",
109         "DS:lookup:COUNTER:25:0:U",
110         "DS:access:COUNTER:25:0:U",
111         "DS:readlink:COUNTER:25:0:U",
112         "DS:read:COUNTER:25:0:U",
113         "DS:write:COUNTER:25:0:U",
114         "DS:create:COUNTER:25:0:U",
115         "DS:mkdir:COUNTER:25:0:U",
116         "DS:symlink:COUNTER:25:0:U",
117         "DS:mknod:COUNTER:25:0:U",
118         "DS:remove:COUNTER:25:0:U",
119         "DS:rmdir:COUNTER:25:0:U",
120         "DS:rename:COUNTER:25:0:U",
121         "DS:link:COUNTER:25:0:U",
122         "DS:readdir:COUNTER:25:0:U",
123         "DS:readdirplus:COUNTER:25:0:U",
124         "DS:fsstat:COUNTER:25:0:U",
125         "DS:fsinfo:COUNTER:25:0:U",
126         "DS:pathconf:COUNTER:25:0:U",
127         "DS:commit:COUNTER:25:0:U",
128         NULL
129 };
130 static int nfs3_procedures_ds_num = 22;
131
132 #ifdef HAVE_LIBKSTAT
133 extern kstat_ctl_t *kc;
134 static kstat_t *nfs2_ksp_client;
135 static kstat_t *nfs2_ksp_server;
136 static kstat_t *nfs3_ksp_client;
137 static kstat_t *nfs3_ksp_server;
138 static kstat_t *nfs4_ksp_client;
139 static kstat_t *nfs4_ksp_server;
140 #endif
141
142 /* Possibly TODO: NFSv4 statistics */
143
144 extern time_t curtime;
145
146 void nfs_init (void)
147 {
148 #ifdef HAVE_LIBKSTAT
149         kstat_t *ksp_chain;
150
151         nfs2_ksp_client = NULL;
152         nfs2_ksp_server = NULL;
153         nfs3_ksp_client = NULL;
154         nfs3_ksp_server = NULL;
155         nfs4_ksp_client = NULL;
156         nfs4_ksp_server = NULL;
157         
158         if (kc == NULL)
159                 return;
160
161         for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
162                         ksp_chain = ksp_chain->ks_next)
163         {
164                 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
165                         continue;
166                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
167                         nfs2_ksp_server = ksp_chain;
168                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
169                         nfs3_ksp_server = ksp_chain;
170                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
171                         nfs4_ksp_server = ksp_chain;
172                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
173                         nfs2_ksp_client = ksp_chain;
174                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
175                         nfs3_ksp_client = ksp_chain;
176                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
177                         nfs4_ksp_client = ksp_chain;
178         }
179 #endif
180
181         return;
182 }
183
184 #define BUFSIZE 1024
185 void nfs2_procedures_write (char *host, char *inst, char *val)
186 {
187         char filename[BUFSIZE];
188
189         if (snprintf (filename, BUFSIZE, nfs2_procedures_file, inst) > BUFSIZE)
190                 return;
191
192         rrd_update_file (host, filename, val, nfs2_procedures_ds_def,
193                         nfs2_procedures_ds_num);
194 }
195
196 void nfs3_procedures_write (char *host, char *inst, char *val)
197 {
198         char filename[BUFSIZE];
199
200         if (snprintf (filename, BUFSIZE, nfs3_procedures_file, inst) > BUFSIZE)
201                 return;
202
203         rrd_update_file (host, filename, val, nfs3_procedures_ds_def,
204                         nfs3_procedures_ds_num);
205 }
206
207 void nfs2_procedures_submit (unsigned long long *val, char *inst)
208 {
209         char buf[BUFSIZE];
210         int retval = 0;
211
212         retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
213                         "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
214                         "%llu:%llu:%llu", /* 18x %llu */
215                         (unsigned int) curtime,
216                         val[0], val[1], val[2], val[3], val[4], val[5], val[6],
217                         val[7], val[8], val[9], val[10], val[11], val[12],
218                         val[13], val[14], val[15], val[16], val[17]);
219
220
221         if (retval >= BUFSIZE)
222                 return;
223         else if (retval < 0)
224         {
225                 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
226                 return;
227         }
228
229         plugin_submit ("nfs2_procedures", inst, buf);
230 }
231
232 void nfs3_procedures_submit (unsigned long long *val, char *inst)
233 {
234         char buf[BUFSIZE];
235         int retval = 0;
236
237         retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
238                         "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
239                         "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
240                         (unsigned int) curtime,
241                         val[0], val[1], val[2], val[3], val[4], val[5], val[6],
242                         val[7], val[8], val[9], val[10], val[11], val[12],
243                         val[13], val[14], val[15], val[16], val[17], val[18],
244                         val[19], val[20], val[21]);
245
246         if (retval >= BUFSIZE)
247                 return;
248         else if (retval < 0)
249         {
250                 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
251                 return;
252         }
253
254         plugin_submit("nfs3_procedures", inst, buf);
255 }
256
257 #if defined(KERNEL_LINUX)
258 void nfs_read_stats_file (FILE *fh, char *inst)
259 {
260         char buffer[BUFSIZE];
261
262         char *fields[48];
263         int numfields = 0;
264
265         if (fh == NULL)
266                 return;
267
268         while (fgets (buffer, BUFSIZE, fh) != NULL)
269         {
270                 numfields = strsplit (buffer, fields, 48);
271
272                 if (numfields < 2)
273                         continue;
274
275                 if (strncmp (fields[0], "proc2", 5) == 0)
276                 {
277                         int i;
278                         unsigned long long *values;
279
280                         if (numfields - 2 != nfs2_procedures_ds_num)
281                         {
282                                 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
283                                 continue;
284                         }
285
286                         if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
287                         {
288                                 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
289                                 continue;
290                         }
291
292                         for (i = 0; i < nfs2_procedures_ds_num; i++)
293                                 values[i] = atoll (fields[i + 2]);
294
295                         nfs2_procedures_submit (values, inst);
296
297                         free (values);
298                 }
299                 else if (strncmp (fields[0], "proc3", 5) == 0)
300                 {
301                         int i;
302                         unsigned long long *values;
303
304                         if (numfields - 2 != nfs3_procedures_ds_num)
305                         {
306                                 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
307                                 continue;
308                         }
309
310                         if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
311                         {
312                                 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
313                                 continue;
314                         }
315
316                         for (i = 0; i < nfs3_procedures_ds_num; i++)
317                                 values[i] = atoll (fields[i + 2]);
318
319                         nfs3_procedures_submit (values, inst);
320
321                         free (values);
322                 }
323         }
324 }
325 #endif /* defined(KERNEL_LINUX) */
326 #undef BUFSIZE
327
328 #ifdef HAVE_LIBKSTAT
329 void nfs2_read_kstat (kstat_t *ksp, char *inst)
330 {
331         unsigned long long values[18];
332
333         values[0] = get_kstat_value (ksp, "null");
334         values[1] = get_kstat_value (ksp, "getattr");
335         values[2] = get_kstat_value (ksp, "setattr");
336         values[3] = get_kstat_value (ksp, "root");
337         values[4] = get_kstat_value (ksp, "lookup");
338         values[5] = get_kstat_value (ksp, "readlink");
339         values[6] = get_kstat_value (ksp, "read");
340         values[7] = get_kstat_value (ksp, "wrcache");
341         values[8] = get_kstat_value (ksp, "write");
342         values[9] = get_kstat_value (ksp, "create");
343         values[10] = get_kstat_value (ksp, "remove");
344         values[11] = get_kstat_value (ksp, "rename");
345         values[12] = get_kstat_value (ksp, "link");
346         values[13] = get_kstat_value (ksp, "symlink");
347         values[14] = get_kstat_value (ksp, "mkdir");
348         values[15] = get_kstat_value (ksp, "rmdir");
349         values[16] = get_kstat_value (ksp, "readdir");
350         values[17] = get_kstat_value (ksp, "statfs");
351
352         nfs2_procedures_submit (values, inst);
353 }
354 #endif
355
356 void nfs_read (void)
357 {
358 #if defined(KERNEL_LINUX)
359         FILE *fh;
360
361         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
362         {
363                 nfs_read_stats_file (fh, "client");
364                 fclose (fh);
365         }
366
367         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
368         {
369                 nfs_read_stats_file (fh, "server");
370                 fclose (fh);
371         }
372
373 /* #endif defined(KERNEL_LINUX) */
374
375 #elif defined(HAVE_LIBKSTAT)
376         if (nfs2_ksp_client != NULL)
377                 nfs2_read_kstat (nfs2_ksp_client, "client");
378         if (nfs2_ksp_server != NULL)
379                 nfs2_read_kstat (nfs2_ksp_server, "server");
380 #endif /* defined(HAVE_LIBKSTAT) */
381 }
382
383 void module_register (void)
384 {
385     plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
386     plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
387     plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
388 }
389
390 #undef MODULE_NAME
391 #endif /* COLLECT_LOAD */