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