Code

a6befa7eef2bfbbaf69c82608eb02f354587306b
[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  **/
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)
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)
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)
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)
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)
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)
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)
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)
225         while (s[0] != '/')
226                 ++s;
228         /* Remove '/' */
229         ++s;
230         return atoll(s);
233 static void vserver_read (void)
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)
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 : */