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