Redesigned the VServer DS's to be more consistent with the rest of collectd.
[collectd.git] / src / vserver.c
1 /**
2  * collectd - src/vserver.c
3  * Copyright (C) 2006  Sebastian Harl
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  *   Sebastian Harl <sh at tokkee.org>
21  **/
22
23 #include "vserver.h"
24
25 #include "collectd.h"
26 #include "common.h"
27 #include "plugin.h"
28
29 #include <dirent.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36
37 #if defined(KERNEL_LINUX)
38 # define VSERVER_HAVE_READ 1
39 #else
40 # define VSERVER_HAVE_READ 0
41 #endif /* defined(KERNEL_LINUX) */
42
43 static char *rrd_unix   = "vserver-%s/traffic-unix.rrd";
44 static char *rrd_inet   = "vserver-%s/traffic-inet.rrd";
45 static char *rrd_inet6  = "vserver-%s/traffic-inet6.rrd";
46 static char *rrd_other  = "vserver-%s/traffic-other.rrd";
47 static char *rrd_unspec = "vserver-%s/traffic-unspec.rrd";
48
49 static char *rrd_thread = "vserver-%s/threads.rrd";
50
51 static char *rrd_load   = "vserver-%s/load.rrd";
52
53 static char *rrd_procs  = "vserver-%s/vs_processes.rrd";
54 static char *rrd_memory = "vserver-%s/vs_memory.rrd";
55
56 /* 9223372036854775807 == LLONG_MAX */
57 /* bytes transferred */
58 static char *ds_def_unix[] =
59 {
60         "DS:incoming:COUNTER:25:0:9223372036854775807",
61         "DS:outgoing:COUNTER:25:0:9223372036854775807",
62         "DS:failed:COUNTER:25:0:9223372036854775807",
63         NULL
64 };
65 static int ds_num_unix = 3;
66
67 static char *ds_def_inet[] =
68 {
69         "DS:incoming:COUNTER:25:0:9223372036854775807",
70         "DS:outgoing:COUNTER:25:0:9223372036854775807",
71         "DS:failed:COUNTER:25:0:9223372036854775807",
72         NULL
73 };
74 static int ds_num_inet = 3;
75
76 static char *ds_def_inet6[] =
77 {
78         "DS:incoming:COUNTER:25:0:9223372036854775807",
79         "DS:outgoing:COUNTER:25:0:9223372036854775807",
80         "DS:failed:COUNTER:25:0:9223372036854775807",
81         NULL
82 };
83 static int ds_num_inet6 = 3;
84
85 static char *ds_def_other[] =
86 {
87         "DS:incoming:COUNTER:25:0:9223372036854775807",
88         "DS:outgoing:COUNTER:25:0:9223372036854775807",
89         "DS:failed:COUNTER:25:0:9223372036854775807",
90         NULL
91 };
92 static int ds_num_other = 3;
93
94 static char *ds_def_unspec[] =
95 {
96         "DS:incoming:COUNTER:25:0:9223372036854775807",
97         "DS:outgoing:COUNTER:25:0:9223372036854775807",
98         "DS:failed:COUNTER:25:0:9223372036854775807",
99         NULL
100 };
101 static int ds_num_unspec = 3;
102
103 static char *ds_def_threads[] =
104 {
105         "DS:total:GAUGE:25:0:65535",
106         "DS:running:GAUGE:25:0:65535",
107         "DS:uninterruptible:GAUGE:25:0:65535",
108         "DS:onhold:GAUGE:25:0:65535",
109         NULL
110 };
111 static int ds_num_threads = 4;
112
113 static char *ds_def_load[] =
114 {
115         "DS:shortterm:GAUGE:25:0:100",
116         "DS:midterm:GAUGE:25:0:100",
117         "DS:longterm:GAUGE:25:0:100",
118         NULL
119 };
120 static int ds_num_load = 3;
121
122 static char *ds_def_procs[] =
123 {
124         "DS:total:GAUGE:25:0:65535",
125         NULL
126 };
127 static int ds_num_procs = 1;
128
129 /* 9223372036854775807 == LLONG_MAX */
130 /* bytes */
131 static char *ds_def_memory[] =
132 {
133         "DS:vm:GAUGE:25:0:9223372036854775807",
134         "DS:vml:GAUGE:25:0:9223372036854775807",
135         "DS:rss:GAUGE:25:0:9223372036854775807",
136         "DS:anon:GAUGE:25:0:9223372036854775807",
137         NULL
138 };
139 static int ds_num_memory = 4;
140
141 static int pagesize = 0;
142
143 static void vserver_init (void)
144 {
145         /* XXX Should we check for getpagesize () in configure?
146          * What's the right thing to do, if there is no getpagesize ()? */
147         pagesize = getpagesize ();
148         return;
149 } /* static void vserver_init(void) */
150
151 static void vserver_unix_write (char *host, char *inst, char *val)
152 {
153         int  len;
154         char filename[BUFSIZE];
155
156         len = snprintf (filename, BUFSIZE, rrd_unix, inst);
157         if ((len > 0) && (len < BUFSIZE))
158                 rrd_update_file (host, filename, val, ds_def_unix, ds_num_unix);
159         return;
160 } /* static void vserver_unix_write(char *host, char *inst, char *val) */
161
162 static void vserver_inet_write (char *host, char *inst, char *val)
163 {
164         int  len;
165         char filename[BUFSIZE];
166
167         len = snprintf (filename, BUFSIZE, rrd_inet, inst);
168         if ((len > 0) && (len < BUFSIZE))
169                 rrd_update_file (host, filename, val, ds_def_inet, ds_num_inet);
170         return;
171 } /* static void vserver_inet_write(char *host, char *inst, char *val) */
172
173 static void vserver_inet6_write (char *host, char *inst, char *val)
174 {
175         int  len;
176         char filename[BUFSIZE];
177
178         len = snprintf (filename, BUFSIZE, rrd_inet6, inst);
179         if ((len > 0) && (len < BUFSIZE))
180                 rrd_update_file (host, filename, val, ds_def_inet6, ds_num_inet6);
181         return;
182 } /* static void vserver_inet6_write(char *host, char *inst, char *val) */
183
184 static void vserver_other_write (char *host, char *inst, char *val)
185 {
186         int  len;
187         char filename[BUFSIZE];
188
189         len = snprintf (filename, BUFSIZE, rrd_other, inst);
190         if ((len > 0) && (len < BUFSIZE))
191                 rrd_update_file (host, filename, val, ds_def_other, ds_num_other);
192         return;
193 } /* static void vserver_other_write(char *host, char *inst, char *val) */
194
195 static void vserver_unspec_write (char *host, char *inst, char *val)
196 {
197         int  len;
198         char filename[BUFSIZE];
199
200         len = snprintf (filename, BUFSIZE, rrd_unspec, inst);
201         if ((len > 0) && (len < BUFSIZE))
202                 rrd_update_file (host, filename, val, ds_def_unspec, ds_num_unspec);
203         return;
204 } /* static void vserver_unspec_write(char *host, char *inst, char *val) */
205
206 static void vserver_threads_write (char *host, char *inst, char *val)
207 {
208         int  len;
209         char filename[BUFSIZE];
210
211         len = snprintf (filename, BUFSIZE, rrd_thread, inst);
212         if ((len > 0) && (len < BUFSIZE))
213                 rrd_update_file (host, filename, val, ds_def_threads, ds_num_threads);
214         return;
215 } /* static void vserver_threads_write(char *host, char *inst, char *val) */
216
217 static void vserver_load_write (char *host, char *inst, char *val)
218 {
219         int  len;
220         char filename[BUFSIZE];
221
222         len = snprintf (filename, BUFSIZE, rrd_load, inst);
223         if ((len > 0) && (len < BUFSIZE))
224                 rrd_update_file (host, filename, val, ds_def_load, ds_num_load);
225         return;
226 } /* static void vserver_load_write(char *host, char *inst, char *val) */
227
228 static void vserver_procs_write (char *host, char *inst, char *val)
229 {
230         int  len;
231         char filename[BUFSIZE];
232
233         len = snprintf (filename, BUFSIZE, rrd_procs, inst);
234         if ((len > 0) && (len < BUFSIZE))
235                 rrd_update_file (host, filename, val, ds_def_procs, ds_num_procs);
236         return;
237 } /* static void vserver_procs_write(char *host, char *inst, char *val) */
238
239 static void vserver_memory_write (char *host, char *inst, char *val)
240 {
241         int  len;
242         char filename[BUFSIZE];
243
244         len = snprintf (filename, BUFSIZE, rrd_memory, inst);
245         if ((len > 0) && (len < BUFSIZE))
246                 rrd_update_file (host, filename, val, ds_def_memory, ds_num_memory);
247         return;
248 } /* static void vserver_memory_write(char *host, char *inst, char *val) */
249
250 #if VSERVER_HAVE_READ
251 static void vserver_submit (char *inst, long long unix_in, long long unix_out, 
252                 long long unix_failed, long long inet_in, long long inet_out, 
253                 long long inet_failed, long long inet6_in, long long inet6_out, 
254                 long long inet6_failed, long long other_in, long long other_out,
255                 long long other_failed, long long unspec_in, long long unspec_out, 
256                 long long unspec_failed, int t_total, int t_running, 
257                 int t_uninterruptible, int t_onhold, double avg1, double avg5, 
258                 double avg15, int p_total, long long vm, long long vml, long long rss, 
259                 long long anon)
260 {
261         int  len;
262         char buffer[BUFSIZE];
263
264         len = snprintf (buffer, BUFSIZE, 
265                         "N:%lld:%lld:%lld", unix_in, unix_out, unix_failed);
266
267         if ((len > 0) && (len < BUFSIZE))
268                 plugin_submit ("vserver_unix", inst, buffer);
269
270
271         len = snprintf (buffer, BUFSIZE, 
272                         "N:%lld:%lld:%lld", inet_in, inet_out, inet_failed);
273
274         if ((len > 0) && (len < BUFSIZE))
275                 plugin_submit ("vserver_inet", inst, buffer);
276
277
278         len = snprintf (buffer, BUFSIZE, 
279                         "N:%lld:%lld:%lld", inet6_in, inet6_out, inet6_failed);
280
281         if ((len > 0) && (len < BUFSIZE))
282                 plugin_submit ("vserver_inet6", inst, buffer);
283
284
285         len = snprintf (buffer, BUFSIZE, 
286                         "N:%lld:%lld:%lld", other_in, other_out, other_failed);
287
288         if ((len > 0) && (len < BUFSIZE))
289                 plugin_submit ("vserver_other", inst, buffer);
290
291
292         len = snprintf (buffer, BUFSIZE, 
293                         "N:%lld:%lld:%lld", unspec_in, unspec_out, unspec_failed);
294
295         if ((len > 0) && (len < BUFSIZE))
296                 plugin_submit ("vserver_unspec", inst, buffer);
297
298
299         len = snprintf (buffer, BUFSIZE, "N:%d:%d:%d:%d",
300                         t_total, t_running, t_uninterruptible, t_onhold);
301
302         if ((len > 0) && (len < BUFSIZE))
303                 plugin_submit ("vserver_threads", inst, buffer);
304
305
306         len = snprintf (buffer, BUFSIZE, "N:%.2f:%.2f:%.2f",
307                         avg1, avg5, avg15);
308
309         if ((len > 0) && (len < BUFSIZE))
310                 plugin_submit ("vserver_load", inst, buffer);
311
312
313         len = snprintf (buffer, BUFSIZE, "N:%d",
314                         p_total);
315
316         if ((len > 0) && (len < BUFSIZE))
317                 plugin_submit ("vserver_procs", inst, buffer);
318
319
320         len = snprintf (buffer, BUFSIZE, "N:%lld:%lld:%lld:%lld",
321                         vm, vml, rss, anon);
322
323         if ((len > 0) && (len < BUFSIZE))
324                 plugin_submit ("vserver_memory", inst, buffer);
325         return;
326 } /* static void vserver_submit() */
327
328 static inline long long __get_sock_bytes(const char *s)
329 {
330         while (s[0] != '/')
331                 ++s;
332
333         /* Remove '/' */
334         ++s;
335         return atoll(s);
336 }
337
338 static void vserver_read (void)
339 {
340         DIR                     *proc;
341         struct dirent   *dent; /* 42 */
342
343         errno = 0;
344         if (NULL == (proc = opendir (PROCDIR))) {
345                 syslog (LOG_ERR, "Cannot open '%s': %s", PROCDIR, strerror (errno));
346                 return;
347         }
348
349         while (NULL != (dent = readdir (proc))) {
350                 int  len;
351                 char file[BUFSIZE];
352
353                 FILE *fh;
354                 char buffer[BUFSIZE];
355
356                 char *cols[4];
357
358                 long long       unix_s[3]       = {-1, -1, -1};
359                 long long       inet[3]         = {-1, -1, -1};
360                 long long       inet6[3]        = {-1, -1, -1};
361                 long long       other[3]        = {-1, -1, -1};
362                 long long       unspec[3]       = {-1, -1, -1};
363                 int                     threads[4]      = {-1, -1, -1, -1};
364                 double          load[3]         = {-1, -1, -1};
365                 /* Just to be consistent ;-) */
366                 int                     procs[1]        = {-1};
367                 long long       memory[4]       = {-1, -1, -1, -1};
368
369                 if (dent->d_name[0] == '.')
370                         continue;
371
372                 /* XXX This check is just the result of a trial-and-error test.
373                  * I did not find any documentation describing the d_type field. */
374                 if (!(dent->d_type & 0x4))
375                         /* This is not a directory */
376                         continue;
377
378                 /* socket message accounting */
379                 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cacct", dent->d_name);
380                 if ((len < 0) || (len >= BUFSIZE))
381                         continue;
382
383                 if (NULL == (fh = fopen (file, "r"))) {
384                         syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
385                         continue;
386                 }
387
388                 while (NULL != fgets (buffer, BUFSIZE, fh)) {
389                         if (strsplit (buffer, cols, 4) < 4)
390                                 continue;
391
392                         if (0 == strcmp (cols[0], "UNIX:")) {
393                                 unix_s[0] = __get_sock_bytes (cols[1]);
394                                 unix_s[1] = __get_sock_bytes (cols[2]);
395                                 unix_s[2] = __get_sock_bytes (cols[3]);
396                         }
397                         else if (0 == strcmp (cols[0], "INET:")) {
398                                 inet[0] = __get_sock_bytes (cols[1]);
399                                 inet[1] = __get_sock_bytes (cols[2]);
400                                 inet[2] = __get_sock_bytes (cols[3]);
401                         }
402                         else if (0 == strcmp (cols[0], "INET6:")) {
403                                 inet6[0] = __get_sock_bytes (cols[1]);
404                                 inet6[1] = __get_sock_bytes (cols[2]);
405                                 inet6[2] = __get_sock_bytes (cols[3]);
406                         }
407                         else if (0 == strcmp (cols[0], "OTHER:")) {
408                                 other[0] = __get_sock_bytes (cols[1]);
409                                 other[1] = __get_sock_bytes (cols[2]);
410                                 other[2] = __get_sock_bytes (cols[3]);
411                         }
412                         else if (0 == strcmp (cols[0], "UNSPEC:")) {
413                                 unspec[0] = __get_sock_bytes (cols[1]);
414                                 unspec[1] = __get_sock_bytes (cols[2]);
415                                 unspec[2] = __get_sock_bytes (cols[3]);
416                         }
417                 }
418
419                 fclose (fh);
420
421                 /* thread information and load */
422                 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cvirt", dent->d_name);
423                 if ((len < 0) || (len >= BUFSIZE))
424                         continue;
425
426                 if (NULL == (fh = fopen (file, "r"))) {
427                         syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
428                         continue;
429                 }
430
431                 while (NULL != fgets (buffer, BUFSIZE, fh)) {
432                         int n = strsplit (buffer, cols, 4);
433
434                         if (2 == n) {
435                                 if (0 == strcmp (cols[0], "nr_threads:")) {
436                                         threads[0] = atoi (cols[1]);
437                                 }
438                                 else if (0 == strcmp (cols[0], "nr_running:")) {
439                                         threads[1] = atoi (cols[1]);
440                                 }
441                                 else if (0 == strcmp (cols[0], "nr_unintr:")) {
442                                         threads[2] = atoi (cols[1]);
443                                 }
444                                 else if (0 == strcmp (cols[0], "nr_onhold:")) {
445                                         threads[3] = atoi (cols[1]);
446                                 }
447                         }
448                         else if (4 == n) {
449                                 if (0 == strcmp (cols[0], "loadavg:")) {
450                                         load[0] = atof (cols[1]);
451                                         load[1] = atof (cols[2]);
452                                         load[2] = atof (cols[3]);
453                                 }
454                         }
455                 }
456
457                 fclose (fh);
458
459                 /* processes and memory usage */
460                 len = snprintf (file, BUFSIZE, PROCDIR "/%s/limit", dent->d_name);
461                 if ((len < 0) || (len >= BUFSIZE))
462                         continue;
463
464                 if (NULL == (fh = fopen (file, "r"))) {
465                         syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
466                         continue;
467                 }
468
469                 while (NULL != fgets (buffer, BUFSIZE, fh)) {
470                         if (strsplit (buffer, cols, 2) < 2)
471                                 continue;
472
473                         if (0 == strcmp (cols[0], "PROC:")) {
474                                 procs[0] = atoi (cols[1]);
475                         }
476                         else if (0 == strcmp (cols[0], "VM:")) {
477                                 memory[0] = atoll (cols[1]) * pagesize;
478                         }
479                         else if (0 == strcmp (cols[0], "VML:")) {
480                                 memory[1] = atoll (cols[1]) * pagesize;
481                         }
482                         else if (0 == strcmp (cols[0], "RSS:")) {
483                                 memory[2] = atoll (cols[1]) * pagesize;
484                         }
485                         else if (0 == strcmp (cols[0], "ANON:")) {
486                                 memory[3] = atoll (cols[1]) * pagesize;
487                         }
488                 }
489
490                 fclose (fh);
491
492                 /* XXX What to do in case of an error (i.e. some value is
493                  * still -1)? */
494
495                 vserver_submit (dent->d_name, unix_s[0], unix_s[1], unix_s[2], 
496                                 inet[0], inet[1], inet[2], inet6[0], inet6[1], inet6[2], 
497                                 other[0], other[1], other[2], unspec[0], unspec[1], unspec[2],
498                                 threads[0], threads[1], threads[2], threads[3], load[0], 
499                                 load[1], load[2], procs[0], memory[0], memory[1], memory[2], 
500                                 memory[3]);
501         }
502
503         closedir (proc);
504         return;
505 } /* static void vserver_read(void) */
506 #else
507 # define vserver_read NULL
508 #endif /* VSERVER_HAVE_READ */
509
510 void module_register (void)
511 {
512         plugin_register (MODULE_NAME, vserver_init, vserver_read, NULL);
513         plugin_register ("vserver_unix", NULL, NULL, vserver_unix_write);
514         plugin_register ("vserver_inet", NULL, NULL, vserver_inet_write);
515         plugin_register ("vserver_inet6", NULL, NULL, vserver_inet6_write);
516         plugin_register ("vserver_other", NULL, NULL, vserver_other_write);
517         plugin_register ("vserver_unspec", NULL, NULL, vserver_unspec_write);
518         plugin_register ("vserver_threads", NULL, NULL, vserver_threads_write);
519         plugin_register ("vserver_load", NULL, NULL, vserver_load_write);
520         plugin_register ("vserver_procs", NULL, NULL, vserver_procs_write);
521         plugin_register ("vserver_memory", NULL, NULL, vserver_memory_write);
522         return;
523 } /* void module_register(void) */
524
525 /* vim: set ts=4 sw=4 noexpandtab : */