90185babc4cc1986257cffa48b9c8636f0814894
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 **/
23 #include "vserver.h"
25 #include "collectd.h"
26 #include "common.h"
27 #include "plugin.h"
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>
37 #if defined(KERNEL_LINUX)
38 # define VSERVER_HAVE_READ 1
39 #else
40 # define VSERVER_HAVE_READ 0
41 #endif /* defined(KERNEL_LINUX) */
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";
49 static char *rrd_thread = "vserver-%s/threads.rrd";
51 static char *rrd_load = "vserver-%s/load.rrd";
53 static char *rrd_procs = "vserver-%s/vs_processes.rrd";
54 static char *rrd_memory = "vserver-%s/vs_memory.rrd";
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;
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;
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;
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;
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;
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;
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;
122 static char *ds_def_procs[] =
123 {
124 "DS:total:GAUGE:25:0:65535",
125 NULL
126 };
127 static int ds_num_procs = 1;
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;
141 static int pagesize = 0;
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) */
151 static void vserver_unix_write (char *host, char *inst, char *val)
152 {
153 int len;
154 char filename[BUFSIZE];
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) */
162 static void vserver_inet_write (char *host, char *inst, char *val)
163 {
164 int len;
165 char filename[BUFSIZE];
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) */
173 static void vserver_inet6_write (char *host, char *inst, char *val)
174 {
175 int len;
176 char filename[BUFSIZE];
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) */
184 static void vserver_other_write (char *host, char *inst, char *val)
185 {
186 int len;
187 char filename[BUFSIZE];
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) */
195 static void vserver_unspec_write (char *host, char *inst, char *val)
196 {
197 int len;
198 char filename[BUFSIZE];
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) */
206 static void vserver_threads_write (char *host, char *inst, char *val)
207 {
208 int len;
209 char filename[BUFSIZE];
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) */
217 static void vserver_load_write (char *host, char *inst, char *val)
218 {
219 int len;
220 char filename[BUFSIZE];
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) */
228 static void vserver_procs_write (char *host, char *inst, char *val)
229 {
230 int len;
231 char filename[BUFSIZE];
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) */
239 static void vserver_memory_write (char *host, char *inst, char *val)
240 {
241 int len;
242 char filename[BUFSIZE];
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) */
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];
264 len = snprintf (buffer, BUFSIZE,
265 "N:%lld:%lld:%lld", unix_in, unix_out, unix_failed);
267 if ((len > 0) && (len < BUFSIZE))
268 plugin_submit ("vserver_unix", inst, buffer);
271 len = snprintf (buffer, BUFSIZE,
272 "N:%lld:%lld:%lld", inet_in, inet_out, inet_failed);
274 if ((len > 0) && (len < BUFSIZE))
275 plugin_submit ("vserver_inet", inst, buffer);
278 len = snprintf (buffer, BUFSIZE,
279 "N:%lld:%lld:%lld", inet6_in, inet6_out, inet6_failed);
281 if ((len > 0) && (len < BUFSIZE))
282 plugin_submit ("vserver_inet6", inst, buffer);
285 len = snprintf (buffer, BUFSIZE,
286 "N:%lld:%lld:%lld", other_in, other_out, other_failed);
288 if ((len > 0) && (len < BUFSIZE))
289 plugin_submit ("vserver_other", inst, buffer);
292 len = snprintf (buffer, BUFSIZE,
293 "N:%lld:%lld:%lld", unspec_in, unspec_out, unspec_failed);
295 if ((len > 0) && (len < BUFSIZE))
296 plugin_submit ("vserver_unspec", inst, buffer);
299 len = snprintf (buffer, BUFSIZE, "N:%d:%d:%d:%d",
300 t_total, t_running, t_uninterruptible, t_onhold);
302 if ((len > 0) && (len < BUFSIZE))
303 plugin_submit ("vserver_threads", inst, buffer);
306 len = snprintf (buffer, BUFSIZE, "N:%.2f:%.2f:%.2f",
307 avg1, avg5, avg15);
309 if ((len > 0) && (len < BUFSIZE))
310 plugin_submit ("vserver_load", inst, buffer);
313 len = snprintf (buffer, BUFSIZE, "N:%d",
314 p_total);
316 if ((len > 0) && (len < BUFSIZE))
317 plugin_submit ("vserver_procs", inst, buffer);
320 len = snprintf (buffer, BUFSIZE, "N:%lld:%lld:%lld:%lld",
321 vm, vml, rss, anon);
323 if ((len > 0) && (len < BUFSIZE))
324 plugin_submit ("vserver_memory", inst, buffer);
325 return;
326 } /* static void vserver_submit() */
328 static inline long long __get_sock_bytes(const char *s)
329 {
330 while (s[0] != '/')
331 ++s;
333 /* Remove '/' */
334 ++s;
335 return atoll(s);
336 }
338 static void vserver_read (void)
339 {
340 DIR *proc;
341 struct dirent *dent; /* 42 */
343 errno = 0;
344 if (NULL == (proc = opendir (PROCDIR))) {
345 syslog (LOG_ERR, "Cannot open '%s': %s", PROCDIR, strerror (errno));
346 return;
347 }
349 while (NULL != (dent = readdir (proc))) {
350 int len;
351 char file[BUFSIZE];
353 FILE *fh;
354 char buffer[BUFSIZE];
356 char *cols[4];
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};
369 if (dent->d_name[0] == '.')
370 continue;
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;
378 /* socket message accounting */
379 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cacct", dent->d_name);
380 if ((len < 0) || (len >= BUFSIZE))
381 continue;
383 if (NULL == (fh = fopen (file, "r"))) {
384 syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
385 continue;
386 }
388 while (NULL != fgets (buffer, BUFSIZE, fh)) {
389 if (strsplit (buffer, cols, 4) < 4)
390 continue;
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 }
419 fclose (fh);
421 /* thread information and load */
422 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cvirt", dent->d_name);
423 if ((len < 0) || (len >= BUFSIZE))
424 continue;
426 if (NULL == (fh = fopen (file, "r"))) {
427 syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
428 continue;
429 }
431 while (NULL != fgets (buffer, BUFSIZE, fh)) {
432 int n = strsplit (buffer, cols, 4);
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 }
457 fclose (fh);
459 /* processes and memory usage */
460 len = snprintf (file, BUFSIZE, PROCDIR "/%s/limit", dent->d_name);
461 if ((len < 0) || (len >= BUFSIZE))
462 continue;
464 if (NULL == (fh = fopen (file, "r"))) {
465 syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
466 continue;
467 }
469 while (NULL != fgets (buffer, BUFSIZE, fh)) {
470 if (strsplit (buffer, cols, 2) < 2)
471 continue;
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 }
490 fclose (fh);
492 /* XXX What to do in case of an error (i.e. some value is
493 * still -1)? */
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 }
503 closedir (proc);
504 return;
505 } /* static void vserver_read(void) */
506 #else
507 # define vserver_read NULL
508 #endif /* VSERVER_HAVE_READ */
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) */
525 /* vim: set ts=4 sw=4 noexpandtab : */