vserver plugin: Converted to the new plugin interface.
[collectd.git] / src / vserver.c
1 /**
2  * collectd - src/vserver.c
3  * Copyright (C) 2006,2007  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 data_source_t octets_dsrc[2] =
47 {
48         {"rx", DS_TYPE_COUNTER, 0, 4294967295.0},
49         {"tx", DS_TYPE_COUNTER, 0, 4294967295.0}
50 };
51
52 static data_set_t octets_ds =
53 {
54         "if_octets", 2, octets_dsrc
55 };
56
57 static data_source_t load_dsrc[3] =
58 {
59         {"shortterm", DS_TYPE_GAUGE, 0.0, 100.0},
60         {"midterm",   DS_TYPE_GAUGE, 0.0, 100.0},
61         {"longterm",  DS_TYPE_GAUGE, 0.0, 100.0}
62 };
63
64 static data_set_t load_ds =
65 {
66         "load", 3, load_dsrc
67 };
68
69 static data_source_t threads_dsrc[1] =
70 {
71         {"value", DS_TYPE_GAUGE, 0.0, 65535.0}
72 };
73
74 static data_set_t threads_ds =
75 {
76         "vs_threads", 1, threads_dsrc
77 };
78
79 static data_source_t processes_dsrc[1] =
80 {
81         {"value", DS_TYPE_GAUGE, 0.0, 65535.0}
82 };
83
84 static data_set_t processes_ds =
85 {
86         "vs_processes", 1, processes_dsrc
87 };
88
89 static data_source_t memory_dsrc[1] =
90 {
91         {"value", DS_TYPE_GAUGE, 0.0, 9223372036854775807.0}
92 };
93
94 static data_set_t memory_ds =
95 {
96         "vs_memory", 1, memory_dsrc
97 };
98
99 #if VSERVER_HAVE_READ
100 static int pagesize = 0;
101
102 static int vserver_init (void)
103 {
104         /* XXX Should we check for getpagesize () in configure?
105          * What's the right thing to do, if there is no getpagesize ()? */
106         pagesize = getpagesize ();
107
108         return (0);
109 } /* static void vserver_init(void) */
110
111 static void traffic_submit (const char *plugin_instance,
112                 const char *type_instance, counter_t rx, counter_t tx)
113 {
114         value_t values[2];
115         value_list_t vl = VALUE_LIST_INIT;
116
117         values[0].counter = rx;
118         values[1].counter = tx;
119
120         vl.values = values;
121         vl.values_len = STATIC_ARRAY_SIZE (values);
122         vl.time = time (NULL);
123         strcpy (vl.host, hostname_g);
124         strcpy (vl.plugin, "vserver");
125         strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
126         strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
127
128         plugin_dispatch_values ("if_octets", &vl);
129 } /* void traffic_submit */
130
131 static void load_submit (const char *plugin_instance,
132                 gauge_t snum, gauge_t mnum, gauge_t lnum)
133 {
134         value_t values[3];
135         value_list_t vl = VALUE_LIST_INIT;
136
137         values[0].gauge = snum;
138         values[1].gauge = mnum;
139         values[2].gauge = lnum;
140
141         vl.values = values;
142         vl.values_len = STATIC_ARRAY_SIZE (values);
143         vl.time = time (NULL);
144         strcpy (vl.host, hostname_g);
145         strcpy (vl.plugin, "vserver");
146         strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
147
148         plugin_dispatch_values ("load", &vl);
149 }
150
151 static void submit_gauge (const char *plugin_instance, const char *type,
152                 const char *type_instance, gauge_t value)
153
154 {
155         value_t values[1];
156         value_list_t vl = VALUE_LIST_INIT;
157
158         values[0].gauge = value;
159
160         vl.values = values;
161         vl.values_len = STATIC_ARRAY_SIZE (values);
162         vl.time = time (NULL);
163         strcpy (vl.host, hostname_g);
164         strcpy (vl.plugin, "vserver");
165         strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
166         strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
167
168         plugin_dispatch_values (type, &vl);
169 } /* void submit_gauge */
170
171 static inline long long __get_sock_bytes(const char *s)
172 {
173         while (s[0] != '/')
174                 ++s;
175
176         /* Remove '/' */
177         ++s;
178         return atoll(s);
179 }
180
181 static int vserver_read (void)
182 {
183         DIR                     *proc;
184         struct dirent   *dent; /* 42 */
185
186         static complain_t complain_obj;
187
188         errno = 0;
189         if (NULL == (proc = opendir (PROCDIR)))
190         {
191                 plugin_complain (LOG_ERR, &complain_obj, "vserver plugin: "
192                                 "fopen (%s) failed: %s", PROCDIR, strerror (errno));
193                 return (-1);
194         }
195         plugin_relief (LOG_NOTICE, &complain_obj, "vserver plugin: "
196                         "fopen (%s) succeeded.", PROCDIR);
197
198         while (NULL != (dent = readdir (proc)))
199         {
200                 int  len;
201                 char file[BUFSIZE];
202
203                 FILE *fh;
204                 char buffer[BUFSIZE];
205
206                 char *cols[4];
207
208                 if (dent->d_name[0] == '.')
209                         continue;
210
211                 /* This is not a directory */
212                 if (dent->d_type != DT_DIR)
213                         continue;
214
215                 /* socket message accounting */
216                 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cacct", dent->d_name);
217                 if ((len < 0) || (len >= BUFSIZE))
218                         continue;
219
220                 if (NULL == (fh = fopen (file, "r")))
221                         syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
222
223                 while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh)))
224                 {
225                         counter_t rx;
226                         counter_t tx;
227                         char *type_instance;
228
229                         if (strsplit (buffer, cols, 4) < 4)
230                                 continue;
231
232                         if (0 == strcmp (cols[0], "UNIX:"))
233                                 type_instance = "unix";
234                         else if (0 == strcmp (cols[0], "INET:"))
235                                 type_instance = "inet";
236                         else if (0 == strcmp (cols[0], "INET6:"))
237                                 type_instance = "inet6";
238                         else if (0 == strcmp (cols[0], "OTHER:"))
239                                 type_instance = "other";
240                         else if (0 == strcmp (cols[0], "UNSPEC:"))
241                                 type_instance = "unspec";
242                         else
243                                 continue;
244
245                         rx = __get_sock_bytes (cols[1]);
246                         tx = __get_sock_bytes (cols[2]);
247                         /* cols[3] == errors */
248
249                         traffic_submit (dent->d_name, type_instance, rx, tx);
250                 } /* while (fgets) */
251
252                 if (fh != NULL)
253                 {
254                         fclose (fh);
255                         fh = NULL;
256                 }
257
258                 /* thread information and load */
259                 len = snprintf (file, BUFSIZE, PROCDIR "/%s/cvirt", dent->d_name);
260                 if ((len < 0) || (len >= BUFSIZE))
261                         continue;
262
263                 if (NULL == (fh = fopen (file, "r")))
264                         syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
265
266                 while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh)))
267                 {
268                         int n = strsplit (buffer, cols, 4);
269
270                         if (2 == n)
271                         {
272                                 char   *type_instance;
273                                 gauge_t value;
274
275                                 if (0 == strcmp (cols[0], "nr_threads:"))
276                                         type_instance = "total";
277                                 else if (0 == strcmp (cols[0], "nr_running:"))
278                                         type_instance = "running";
279                                 else if (0 == strcmp (cols[0], "nr_unintr:"))
280                                         type_instance = "uninterruptable";
281                                 else if (0 == strcmp (cols[0], "nr_onhold:"))
282                                         type_instance = "onhold";
283                                 else
284                                         continue;
285
286                                 value = atof (cols[1]);
287                                 submit_gauge (dent->d_name, "vs_threads", type_instance, value);
288                         }
289                         else if (4 == n) {
290                                 if (0 == strcmp (cols[0], "loadavg:"))
291                                 {
292                                         gauge_t snum = atof (cols[1]);
293                                         gauge_t mnum = atof (cols[2]);
294                                         gauge_t lnum = atof (cols[3]);
295                                         load_submit (dent->d_name, snum, mnum, lnum);
296                                 }
297                         }
298                 } /* while (fgets) */
299
300                 if (fh != NULL)
301                 {
302                         fclose (fh);
303                         fh = NULL;
304                 }
305
306                 /* processes and memory usage */
307                 len = snprintf (file, BUFSIZE, PROCDIR "/%s/limit", dent->d_name);
308                 if ((len < 0) || (len >= BUFSIZE))
309                         continue;
310
311                 if (NULL == (fh = fopen (file, "r")))
312                         syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
313
314                 while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh)))
315                 {
316                         char *type = "vs_memory";
317                         char *type_instance;
318                         gauge_t value;
319
320                         if (strsplit (buffer, cols, 2) < 2)
321                                 continue;
322
323                         if (0 == strcmp (cols[0], "PROC:"))
324                         {
325                                 type = "vs_processes";
326                                 type_instance = "";
327                                 value = atof (cols[1]);
328                         }
329                         else
330                         {
331                                 if (0 == strcmp (cols[0], "VM:"))
332                                         type_instance = "vm";
333                                 else if (0 == strcmp (cols[0], "VML:"))
334                                         type_instance = "vml";
335                                 else if (0 == strcmp (cols[0], "RSS:"))
336                                         type_instance = "rss";
337                                 else if (0 == strcmp (cols[0], "ANON:"))
338                                         type_instance = "anon";
339                                 else
340                                         continue;
341
342                                 value = atof (cols[1]) * pagesize;
343                         }
344
345                         submit_gauge (dent->d_name, type, type_instance, value);
346                 } /* while (fgets) */
347
348                 if (fh != NULL)
349                 {
350                         fclose (fh);
351                         fh = NULL;
352                 }
353         } /* while (readdir) */
354
355         closedir (proc);
356
357         return (0);
358 } /* int vserver_read */
359 #endif /* VSERVER_HAVE_READ */
360
361 void module_register (void)
362 {
363         plugin_register_data_set (&octets_ds);
364         plugin_register_data_set (&load_ds);
365         plugin_register_data_set (&threads_ds);
366         plugin_register_data_set (&processes_ds);
367         plugin_register_data_set (&memory_ds);
368
369 #if VSERVER_HAVE_READ
370         plugin_register_init ("vserver", vserver_init);
371         plugin_register_read ("vserver", vserver_read);
372 #endif /* VSERVER_HAVE_READ */
373 } /* void module_register(void) */
374
375 /* vim: set ts=4 sw=4 noexpandtab : */