a6befa7eef2bfbbaf69c82608eb02f354587306b
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 "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
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>
35 #define BUFSIZE 512
37 #define MODULE_NAME "vserver"
38 #define PROCDIR "/proc/virtual"
40 #if defined(KERNEL_LINUX)
41 # define VSERVER_HAVE_READ 1
42 #else
43 # define VSERVER_HAVE_READ 0
44 #endif /* defined(KERNEL_LINUX) */
46 static char *rrd_socket = "vserver-%s/socket.rrd";
47 static char *rrd_thread = "vserver-%s/threads.rrd";
48 static char *rrd_load = "vserver-%s/load.rrd";
49 static char *rrd_procs = "vserver-%s/processes.rrd";
50 static char *rrd_memory = "vserver-%s/memory.rrd";
52 /* 9223372036854775807 == LLONG_MAX */
53 /* bytes transferred */
54 static char *ds_def_socket[] =
55 {
56 "DS:unix_in:COUNTER:25:0:9223372036854775807",
57 "DS:unix_out:COUNTER:25:0:9223372036854775807",
58 "DS:inet_in:COUNTER:25:0:9223372036854775807",
59 "DS:inet_out:COUNTER:25:0:9223372036854775807",
60 "DS:inet6_in:COUNTER:25:0:9223372036854775807",
61 "DS:inet6_out:COUNTER:25:0:9223372036854775807",
62 "DS:other_in:COUNTER:25:0:9223372036854775807",
63 "DS:other_out:COUNTER:25:0:9223372036854775807",
64 "DS:unspec_in:COUNTER:25:0:9223372036854775807",
65 "DS:unspec_out:COUNTER:25:0:9223372036854775807",
66 NULL
67 };
68 static int ds_num_socket = 10;
70 static char *ds_def_threads[] =
71 {
72 "DS:total:GAUGE:25:0:65535",
73 "DS:running:GAUGE:25:0:65535",
74 "DS:uninterruptible:GAUGE:25:0:65535",
75 "DS:onhold:GAUGE:25:0:65535",
76 NULL
77 };
78 static int ds_num_threads = 4;
80 static char *ds_def_load[] =
81 {
82 "DS:avg1:GAUGE:25:0:100",
83 "DS:avg5:GAUGE:25:0:100",
84 "DS:avg15:GAUGE:25:0:100",
85 NULL
86 };
87 static int ds_num_load = 3;
89 static char *ds_def_procs[] =
90 {
91 "DS:total:GAUGE:25:0:65535",
92 NULL
93 };
94 static int ds_num_procs = 1;
96 /* 9223372036854775807 == LLONG_MAX */
97 /* bytes */
98 static char *ds_def_memory[] =
99 {
100 "DS:vm:GAUGE:25:0:9223372036854775807",
101 "DS:vml:GAUGE:25:0:9223372036854775807",
102 "DS:rss:GAUGE:25:0:9223372036854775807",
103 "DS:anon:GAUGE:25:0:9223372036854775807",
104 NULL
105 };
106 static int ds_num_memory = 4;
108 static int pagesize = 0;
110 static void vserver_init (void)
111 {
112 /* XXX Should we check for getpagesize () in configure?
113 * What's the right thing to do, if there is no getpagesize ()? */
114 pagesize = getpagesize ();
115 return;
116 } /* static void vserver_init(void) */
118 static void vserver_socket_write (char *host, char *inst, char *val)
119 {
120 int len;
121 char filename[BUFSIZE];
123 len = snprintf (filename, BUFSIZE, rrd_socket, inst);
124 if ((len > 0) && (len < BUFSIZE))
125 rrd_update_file (host, filename, val, ds_def_socket, ds_num_socket);
126 return;
127 } /* static void vserver_socket_write(char *host, char *inst, char *val) */
129 static void vserver_threads_write (char *host, char *inst, char *val)
130 {
131 int len;
132 char filename[BUFSIZE];
134 len = snprintf (filename, BUFSIZE, rrd_thread, inst);
135 if ((len > 0) && (len < BUFSIZE))
136 rrd_update_file (host, filename, val, ds_def_threads, ds_num_threads);
137 return;
138 } /* static void vserver_threads_write(char *host, char *inst, char *val) */
140 static void vserver_load_write (char *host, char *inst, char *val)
141 {
142 int len;
143 char filename[BUFSIZE];
145 len = snprintf (filename, BUFSIZE, rrd_load, inst);
146 if ((len > 0) && (len < BUFSIZE))
147 rrd_update_file (host, filename, val, ds_def_load, ds_num_load);
148 return;
149 } /* static void vserver_load_write(char *host, char *inst, char *val) */
151 static void vserver_procs_write (char *host, char *inst, char *val)
152 {
153 int len;
154 char filename[BUFSIZE];
156 len = snprintf (filename, BUFSIZE, rrd_procs, inst);
157 if ((len > 0) && (len < BUFSIZE))
158 rrd_update_file (host, filename, val, ds_def_procs, ds_num_procs);
159 return;
160 } /* static void vserver_procs_write(char *host, char *inst, char *val) */
162 static void vserver_memory_write (char *host, char *inst, char *val)
163 {
164 int len;
165 char filename[BUFSIZE];
167 len = snprintf (filename, BUFSIZE, rrd_memory, inst);
168 if ((len > 0) && (len < BUFSIZE))
169 rrd_update_file (host, filename, val, ds_def_memory, ds_num_memory);
170 return;
171 } /* static void vserver_memory_write(char *host, char *inst, char *val) */
173 #if VSERVER_HAVE_READ
174 static void vserver_submit (char *inst, long long unix_in, long long unix_out,
175 long long inet_in, long long inet_out, long long inet6_in,
176 long long inet6_out, long long other_in, long long other_out,
177 long long unspec_in, long long unspec_out, int t_total, int t_running,
178 int t_uninterruptible, int t_onhold, double avg1, double avg5,
179 double avg15, int p_total, long long vm, long long vml, long long rss,
180 long long anon)
181 {
182 int len;
183 char buffer[BUFSIZE];
185 len = snprintf (buffer, BUFSIZE,
186 "N:%lld:%lld:%lld:%lld:%lld:%lld:%lld:%lld:%lld:%lld",
187 unix_in, unix_out, inet_in, inet_out, inet6_in, inet6_out,
188 other_in, other_out, unspec_in, unspec_out);
190 if ((len > 0) && (len < BUFSIZE))
191 plugin_submit ("vserver_socket", inst, buffer);
194 len = snprintf (buffer, BUFSIZE, "N:%d:%d:%d:%d",
195 t_total, t_running, t_uninterruptible, t_onhold);
197 if ((len > 0) && (len < BUFSIZE))
198 plugin_submit ("vserver_threads", inst, buffer);
201 len = snprintf (buffer, BUFSIZE, "N:%.2f:%.2f:%.2f",
202 avg1, avg5, avg15);
204 if ((len > 0) && (len < BUFSIZE))
205 plugin_submit ("vserver_load", inst, buffer);
208 len = snprintf (buffer, BUFSIZE, "N:%d",
209 p_total);
211 if ((len > 0) && (len < BUFSIZE))
212 plugin_submit ("vserver_procs", inst, buffer);
215 len = snprintf (buffer, BUFSIZE, "N:%lld:%lld:%lld:%lld",
216 vm, vml, rss, anon);
218 if ((len > 0) && (len < BUFSIZE))
219 plugin_submit ("vserver_memory", inst, buffer);
220 return;
221 } /* static void vserver_submit() */
223 static inline long long __get_sock_bytes(const char *s)
224 {
225 while (s[0] != '/')
226 ++s;
228 /* Remove '/' */
229 ++s;
230 return atoll(s);
231 }
233 static void vserver_read (void)
234 {
235 DIR *proc;
236 struct dirent *dent; /* 42 */
238 errno = 0;
239 if (NULL == (proc = opendir (PROCDIR))) {
240 syslog (LOG_ERR, "Cannot open '%s': %s", PROCDIR, strerror (errno));
241 return;
242 }
244 while (NULL != (dent = readdir (proc))) {
245 int len;
246 char file[BUFSIZE];
248 FILE *fh;
249 char buffer[BUFSIZE];
251 char *cols[4];
253 long long socket[10] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
254 int threads[4] = {-1, -1, -1, -1};
255 double load[3] = {-1, -1, -1};
256 /* Just to be consistent ;-) */
257 int procs[1] = {-1};
258 long long memory[4] = {-1, -1, -1, -1};
260 if (dent->d_name[0] == '.')
261 continue;
263 /* XXX This check is just the result of a trial-and-error test.
264 * I did not find any documentation describing the d_type field. */
265 if (!(dent->d_type & 0x4))
266 /* This is not a directory */
267 continue;
269 /* socket message accounting */
270 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cacct", dent->d_name);
271 if ((len < 0) || (len >= BUFSIZE))
272 continue;
274 if (NULL == (fh = fopen (file, "r"))) {
275 syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
276 continue;
277 }
279 while (NULL != fgets (buffer, BUFSIZE, fh)) {
280 if (strsplit (buffer, cols, 3) < 3)
281 continue;
283 if (0 == strcmp (cols[0], "UNIX:")) {
284 socket[0] = __get_sock_bytes (cols[1]);
285 socket[1] = __get_sock_bytes (cols[2]);
286 }
287 else if (0 == strcmp (cols[0], "INET:")) {
288 socket[2] = __get_sock_bytes (cols[1]);
289 socket[3] = __get_sock_bytes (cols[2]);
290 }
291 else if (0 == strcmp (cols[0], "INET6:")) {
292 socket[4] = __get_sock_bytes (cols[1]);
293 socket[5] = __get_sock_bytes (cols[2]);
294 }
295 else if (0 == strcmp (cols[0], "OTHER:")) {
296 socket[6] = __get_sock_bytes (cols[1]);
297 socket[7] = __get_sock_bytes (cols[2]);
298 }
299 else if (0 == strcmp (cols[0], "UNSPEC:")) {
300 socket[8] = __get_sock_bytes (cols[1]);
301 socket[9] = __get_sock_bytes (cols[2]);
302 }
303 }
305 fclose (fh);
307 /* thread information and load */
308 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cvirt", dent->d_name);
309 if ((len < 0) || (len >= BUFSIZE))
310 continue;
312 if (NULL == (fh = fopen (file, "r"))) {
313 syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
314 continue;
315 }
317 while (NULL != fgets (buffer, BUFSIZE, fh)) {
318 int n = strsplit (buffer, cols, 4);
320 if (2 == n) {
321 if (0 == strcmp (cols[0], "nr_threads:")) {
322 threads[0] = atoi (cols[1]);
323 }
324 else if (0 == strcmp (cols[0], "nr_running:")) {
325 threads[1] = atoi (cols[1]);
326 }
327 else if (0 == strcmp (cols[0], "nr_unintr:")) {
328 threads[2] = atoi (cols[1]);
329 }
330 else if (0 == strcmp (cols[0], "nr_onhold:")) {
331 threads[3] = atoi (cols[1]);
332 }
333 }
334 else if (4 == n) {
335 if (0 == strcmp (cols[0], "loadavg:")) {
336 load[0] = atof (cols[1]);
337 load[1] = atof (cols[2]);
338 load[2] = atof (cols[3]);
339 }
340 }
341 }
343 fclose (fh);
345 /* processes and memory usage */
346 len = snprintf (file, BUFSIZE, PROCDIR "/%s/limit", dent->d_name);
347 if ((len < 0) || (len >= BUFSIZE))
348 continue;
350 if (NULL == (fh = fopen (file, "r"))) {
351 syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
352 continue;
353 }
355 while (NULL != fgets (buffer, BUFSIZE, fh)) {
356 if (strsplit (buffer, cols, 2) < 2)
357 continue;
359 if (0 == strcmp (cols[0], "PROC:")) {
360 procs[0] = atoi (cols[1]);
361 }
362 else if (0 == strcmp (cols[0], "VM:")) {
363 memory[0] = atoll (cols[1]) * pagesize;
364 }
365 else if (0 == strcmp (cols[0], "VML:")) {
366 memory[1] = atoll (cols[1]) * pagesize;
367 }
368 else if (0 == strcmp (cols[0], "RSS:")) {
369 memory[2] = atoll (cols[1]) * pagesize;
370 }
371 else if (0 == strcmp (cols[0], "ANON:")) {
372 memory[3] = atoll (cols[1]) * pagesize;
373 }
374 }
376 fclose (fh);
378 /* XXX What to do in case of an error (i.e. some value is
379 * still -1)? */
381 vserver_submit (dent->d_name, socket[0], socket[1], socket[2],
382 socket[3], socket[4], socket[5], socket[6], socket[7],
383 socket[8], socket[9], threads[0], threads[1], threads[2],
384 threads[3], load[0], load[1], load[2], procs[0], memory[0],
385 memory[1], memory[2], memory[3]);
386 }
388 closedir (proc);
389 return;
390 } /* static void vserver_read(void) */
391 #else
392 # define vserver_read NULL
393 #endif /* VSERVER_HAVE_READ */
395 void module_register (void)
396 {
397 plugin_register (MODULE_NAME, vserver_init, vserver_read, NULL);
398 plugin_register ("vserver_socket", NULL, NULL, vserver_socket_write);
399 plugin_register ("vserver_threads", NULL, NULL, vserver_threads_write);
400 plugin_register ("vserver_load", NULL, NULL, vserver_load_write);
401 plugin_register ("vserver_procs", NULL, NULL, vserver_procs_write);
402 plugin_register ("vserver_memory", NULL, NULL, vserver_memory_write);
403 return;
404 } /* void module_register(void) */
406 /* vim: set ts=4 sw=4 noexpandtab : */