Merge branch 'collectd-5.7'
[collectd.git] / src / vserver.c
1 /**
2  * collectd - src/vserver.c
3  * Copyright (C) 2006,2007  Sebastian Harl
4  * Copyright (C) 2007-2010  Florian octo Forster
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *   Sebastian Harl <sh at tokkee.org>
26  *   Florian octo Forster <octo at collectd.org>
27  **/
28
29 #include "collectd.h"
30
31 #include "common.h"
32 #include "plugin.h"
33
34 #include <dirent.h>
35 #include <sys/types.h>
36
37 #define BUFSIZE 512
38
39 #define PROCDIR "/proc/virtual"
40
41 #if !KERNEL_LINUX
42 #error "No applicable input method."
43 #endif
44
45 static int pagesize = 0;
46
47 static int vserver_init(void) {
48   /* XXX Should we check for getpagesize () in configure?
49    * What's the right thing to do, if there is no getpagesize ()? */
50   pagesize = getpagesize();
51
52   return 0;
53 } /* static void vserver_init(void) */
54
55 static void traffic_submit(const char *plugin_instance,
56                            const char *type_instance, derive_t rx,
57                            derive_t tx) {
58   value_list_t vl = VALUE_LIST_INIT;
59   value_t values[] = {
60       {.derive = rx}, {.derive = tx},
61   };
62
63   vl.values = values;
64   vl.values_len = STATIC_ARRAY_SIZE(values);
65   sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
66   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
67   sstrncpy(vl.type, "if_octets", sizeof(vl.type));
68   sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
69
70   plugin_dispatch_values(&vl);
71 } /* void traffic_submit */
72
73 static void load_submit(const char *plugin_instance, gauge_t snum, gauge_t mnum,
74                         gauge_t lnum) {
75   value_list_t vl = VALUE_LIST_INIT;
76   value_t values[] = {
77       {.gauge = snum}, {.gauge = mnum}, {.gauge = lnum},
78   };
79
80   vl.values = values;
81   vl.values_len = STATIC_ARRAY_SIZE(values);
82   sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
83   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
84   sstrncpy(vl.type, "load", sizeof(vl.type));
85
86   plugin_dispatch_values(&vl);
87 }
88
89 static void submit_gauge(const char *plugin_instance, const char *type,
90                          const char *type_instance, gauge_t value)
91
92 {
93   value_list_t vl = VALUE_LIST_INIT;
94
95   vl.values = &(value_t){.gauge = value};
96   vl.values_len = 1;
97   sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
98   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
99   sstrncpy(vl.type, type, sizeof(vl.type));
100   sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
101
102   plugin_dispatch_values(&vl);
103 } /* void submit_gauge */
104
105 static derive_t vserver_get_sock_bytes(const char *s) {
106   value_t v;
107   int status;
108
109   while (s[0] != '/')
110     ++s;
111
112   /* Remove '/' */
113   ++s;
114
115   status = parse_value(s, &v, DS_TYPE_DERIVE);
116   if (status != 0)
117     return -1;
118   return v.derive;
119 }
120
121 static int vserver_read(void) {
122   DIR *proc;
123
124   errno = 0;
125   proc = opendir(PROCDIR);
126   if (proc == NULL) {
127     char errbuf[1024];
128     ERROR("vserver plugin: fopen (%s): %s", PROCDIR,
129           sstrerror(errno, errbuf, sizeof(errbuf)));
130     return -1;
131   }
132
133   while (42) {
134     struct dirent *dent;
135     int len;
136     char file[BUFSIZE];
137
138     FILE *fh;
139     char buffer[BUFSIZE];
140
141     struct stat statbuf;
142     char *cols[4];
143
144     int status;
145
146     errno = 0;
147     dent = readdir(proc);
148     if (dent == NULL) {
149       char errbuf[4096];
150
151       if (errno == 0) /* end of directory */
152         break;
153
154       ERROR("vserver plugin: failed to read directory %s: %s", PROCDIR,
155             sstrerror(errno, errbuf, sizeof(errbuf)));
156       closedir(proc);
157       return -1;
158     }
159
160     if (dent->d_name[0] == '.')
161       continue;
162
163     len = snprintf(file, sizeof(file), PROCDIR "/%s", dent->d_name);
164     if ((len < 0) || (len >= BUFSIZE))
165       continue;
166
167     status = stat(file, &statbuf);
168     if (status != 0) {
169       char errbuf[4096];
170       WARNING("vserver plugin: stat (%s) failed: %s", file,
171               sstrerror(errno, errbuf, sizeof(errbuf)));
172       continue;
173     }
174
175     if (!S_ISDIR(statbuf.st_mode))
176       continue;
177
178     /* socket message accounting */
179     len = snprintf(file, sizeof(file), PROCDIR "/%s/cacct", dent->d_name);
180     if ((len < 0) || ((size_t)len >= sizeof(file)))
181       continue;
182
183     if (NULL == (fh = fopen(file, "r"))) {
184       char errbuf[1024];
185       ERROR("Cannot open '%s': %s", file,
186             sstrerror(errno, errbuf, sizeof(errbuf)));
187     }
188
189     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
190       derive_t rx;
191       derive_t tx;
192       const char *type_instance;
193
194       if (strsplit(buffer, cols, 4) < 4)
195         continue;
196
197       if (0 == strcmp(cols[0], "UNIX:"))
198         type_instance = "unix";
199       else if (0 == strcmp(cols[0], "INET:"))
200         type_instance = "inet";
201       else if (0 == strcmp(cols[0], "INET6:"))
202         type_instance = "inet6";
203       else if (0 == strcmp(cols[0], "OTHER:"))
204         type_instance = "other";
205       else if (0 == strcmp(cols[0], "UNSPEC:"))
206         type_instance = "unspec";
207       else
208         continue;
209
210       rx = vserver_get_sock_bytes(cols[1]);
211       tx = vserver_get_sock_bytes(cols[2]);
212       /* cols[3] == errors */
213
214       traffic_submit(dent->d_name, type_instance, rx, tx);
215     } /* while (fgets) */
216
217     if (fh != NULL) {
218       fclose(fh);
219       fh = NULL;
220     }
221
222     /* thread information and load */
223     len = snprintf(file, sizeof(file), PROCDIR "/%s/cvirt", dent->d_name);
224     if ((len < 0) || ((size_t)len >= sizeof(file)))
225       continue;
226
227     if (NULL == (fh = fopen(file, "r"))) {
228       char errbuf[1024];
229       ERROR("Cannot open '%s': %s", file,
230             sstrerror(errno, errbuf, sizeof(errbuf)));
231     }
232
233     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
234       int n = strsplit(buffer, cols, 4);
235
236       if (2 == n) {
237         const char *type_instance;
238         gauge_t value;
239
240         if (0 == strcmp(cols[0], "nr_threads:"))
241           type_instance = "total";
242         else if (0 == strcmp(cols[0], "nr_running:"))
243           type_instance = "running";
244         else if (0 == strcmp(cols[0], "nr_unintr:"))
245           type_instance = "uninterruptable";
246         else if (0 == strcmp(cols[0], "nr_onhold:"))
247           type_instance = "onhold";
248         else
249           continue;
250
251         value = atof(cols[1]);
252         submit_gauge(dent->d_name, "vs_threads", type_instance, value);
253       } else if (4 == n) {
254         if (0 == strcmp(cols[0], "loadavg:")) {
255           gauge_t snum = atof(cols[1]);
256           gauge_t mnum = atof(cols[2]);
257           gauge_t lnum = atof(cols[3]);
258           load_submit(dent->d_name, snum, mnum, lnum);
259         }
260       }
261     } /* while (fgets) */
262
263     if (fh != NULL) {
264       fclose(fh);
265       fh = NULL;
266     }
267
268     /* processes and memory usage */
269     len = snprintf(file, sizeof(file), PROCDIR "/%s/limit", dent->d_name);
270     if ((len < 0) || ((size_t)len >= sizeof(file)))
271       continue;
272
273     if (NULL == (fh = fopen(file, "r"))) {
274       char errbuf[1024];
275       ERROR("Cannot open '%s': %s", file,
276             sstrerror(errno, errbuf, sizeof(errbuf)));
277     }
278
279     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
280       const char *type = "vs_memory";
281       const char *type_instance;
282       gauge_t value;
283
284       if (strsplit(buffer, cols, 2) < 2)
285         continue;
286
287       if (0 == strcmp(cols[0], "PROC:")) {
288         type = "vs_processes";
289         type_instance = "";
290         value = atof(cols[1]);
291       } else {
292         if (0 == strcmp(cols[0], "VM:"))
293           type_instance = "vm";
294         else if (0 == strcmp(cols[0], "VML:"))
295           type_instance = "vml";
296         else if (0 == strcmp(cols[0], "RSS:"))
297           type_instance = "rss";
298         else if (0 == strcmp(cols[0], "ANON:"))
299           type_instance = "anon";
300         else
301           continue;
302
303         value = atof(cols[1]) * pagesize;
304       }
305
306       submit_gauge(dent->d_name, type, type_instance, value);
307     } /* while (fgets) */
308
309     if (fh != NULL) {
310       fclose(fh);
311       fh = NULL;
312     }
313   } /* while (readdir) */
314
315   closedir(proc);
316
317   return 0;
318 } /* int vserver_read */
319
320 void module_register(void) {
321   plugin_register_init("vserver", vserver_init);
322   plugin_register_read("vserver", vserver_read);
323 } /* void module_register(void) */