Merge branch 'collectd-5.8'
[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;
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     ERROR("vserver plugin: fopen (%s): %s", PROCDIR, STRERRNO);
128     return -1;
129   }
130
131   while (42) {
132     struct dirent *dent;
133     int len;
134     char file[BUFSIZE];
135
136     FILE *fh;
137     char buffer[BUFSIZE];
138
139     struct stat statbuf;
140     char *cols[4];
141
142     int status;
143
144     errno = 0;
145     dent = readdir(proc);
146     if (dent == NULL) {
147       if (errno == 0) /* end of directory */
148         break;
149
150       ERROR("vserver plugin: failed to read directory %s: %s", PROCDIR,
151             STRERRNO);
152       closedir(proc);
153       return -1;
154     }
155
156     if (dent->d_name[0] == '.')
157       continue;
158
159     len = snprintf(file, sizeof(file), PROCDIR "/%s", dent->d_name);
160     if ((len < 0) || (len >= BUFSIZE))
161       continue;
162
163     status = stat(file, &statbuf);
164     if (status != 0) {
165       WARNING("vserver plugin: stat (%s) failed: %s", file, STRERRNO);
166       continue;
167     }
168
169     if (!S_ISDIR(statbuf.st_mode))
170       continue;
171
172     /* socket message accounting */
173     len = snprintf(file, sizeof(file), PROCDIR "/%s/cacct", dent->d_name);
174     if ((len < 0) || ((size_t)len >= sizeof(file)))
175       continue;
176
177     if (NULL == (fh = fopen(file, "r"))) {
178       ERROR("Cannot open '%s': %s", file, STRERRNO);
179     }
180
181     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
182       derive_t rx;
183       derive_t tx;
184       const char *type_instance;
185
186       if (strsplit(buffer, cols, 4) < 4)
187         continue;
188
189       if (0 == strcmp(cols[0], "UNIX:"))
190         type_instance = "unix";
191       else if (0 == strcmp(cols[0], "INET:"))
192         type_instance = "inet";
193       else if (0 == strcmp(cols[0], "INET6:"))
194         type_instance = "inet6";
195       else if (0 == strcmp(cols[0], "OTHER:"))
196         type_instance = "other";
197       else if (0 == strcmp(cols[0], "UNSPEC:"))
198         type_instance = "unspec";
199       else
200         continue;
201
202       rx = vserver_get_sock_bytes(cols[1]);
203       tx = vserver_get_sock_bytes(cols[2]);
204       /* cols[3] == errors */
205
206       traffic_submit(dent->d_name, type_instance, rx, tx);
207     } /* while (fgets) */
208
209     if (fh != NULL) {
210       fclose(fh);
211       fh = NULL;
212     }
213
214     /* thread information and load */
215     len = snprintf(file, sizeof(file), PROCDIR "/%s/cvirt", dent->d_name);
216     if ((len < 0) || ((size_t)len >= sizeof(file)))
217       continue;
218
219     if (NULL == (fh = fopen(file, "r"))) {
220       ERROR("Cannot open '%s': %s", file, STRERRNO);
221     }
222
223     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
224       int n = strsplit(buffer, cols, 4);
225
226       if (2 == n) {
227         const char *type_instance;
228         gauge_t value;
229
230         if (0 == strcmp(cols[0], "nr_threads:"))
231           type_instance = "total";
232         else if (0 == strcmp(cols[0], "nr_running:"))
233           type_instance = "running";
234         else if (0 == strcmp(cols[0], "nr_unintr:"))
235           type_instance = "uninterruptable";
236         else if (0 == strcmp(cols[0], "nr_onhold:"))
237           type_instance = "onhold";
238         else
239           continue;
240
241         value = atof(cols[1]);
242         submit_gauge(dent->d_name, "vs_threads", type_instance, value);
243       } else if (4 == n) {
244         if (0 == strcmp(cols[0], "loadavg:")) {
245           gauge_t snum = atof(cols[1]);
246           gauge_t mnum = atof(cols[2]);
247           gauge_t lnum = atof(cols[3]);
248           load_submit(dent->d_name, snum, mnum, lnum);
249         }
250       }
251     } /* while (fgets) */
252
253     if (fh != NULL) {
254       fclose(fh);
255       fh = NULL;
256     }
257
258     /* processes and memory usage */
259     len = snprintf(file, sizeof(file), PROCDIR "/%s/limit", dent->d_name);
260     if ((len < 0) || ((size_t)len >= sizeof(file)))
261       continue;
262
263     if (NULL == (fh = fopen(file, "r"))) {
264       ERROR("Cannot open '%s': %s", file, STRERRNO);
265     }
266
267     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
268       const char *type = "vs_memory";
269       const char *type_instance;
270       gauge_t value;
271
272       if (strsplit(buffer, cols, 2) < 2)
273         continue;
274
275       if (0 == strcmp(cols[0], "PROC:")) {
276         type = "vs_processes";
277         type_instance = "";
278         value = atof(cols[1]);
279       } else {
280         if (0 == strcmp(cols[0], "VM:"))
281           type_instance = "vm";
282         else if (0 == strcmp(cols[0], "VML:"))
283           type_instance = "vml";
284         else if (0 == strcmp(cols[0], "RSS:"))
285           type_instance = "rss";
286         else if (0 == strcmp(cols[0], "ANON:"))
287           type_instance = "anon";
288         else
289           continue;
290
291         value = atof(cols[1]) * pagesize;
292       }
293
294       submit_gauge(dent->d_name, type, type_instance, value);
295     } /* while (fgets) */
296
297     if (fh != NULL) {
298       fclose(fh);
299       fh = NULL;
300     }
301   } /* while (readdir) */
302
303   closedir(proc);
304
305   return 0;
306 } /* int vserver_read */
307
308 void module_register(void) {
309   plugin_register_init("vserver", vserver_init);
310   plugin_register_read("vserver", vserver_read);
311 } /* void module_register(void) */