Fix compile time issues
[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 "plugin.h"
32 #include "utils/common/common.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},
61       {.derive = tx},
62   };
63
64   vl.values = values;
65   vl.values_len = STATIC_ARRAY_SIZE(values);
66   sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
67   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
68   sstrncpy(vl.type, "if_octets", sizeof(vl.type));
69   sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
70
71   plugin_dispatch_values(&vl);
72 } /* void traffic_submit */
73
74 static void load_submit(const char *plugin_instance, gauge_t snum, gauge_t mnum,
75                         gauge_t lnum) {
76   value_list_t vl = VALUE_LIST_INIT;
77   value_t values[] = {
78       {.gauge = snum},
79       {.gauge = mnum},
80       {.gauge = lnum},
81   };
82
83   vl.values = values;
84   vl.values_len = STATIC_ARRAY_SIZE(values);
85   sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
86   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
87   sstrncpy(vl.type, "load", sizeof(vl.type));
88
89   plugin_dispatch_values(&vl);
90 }
91
92 static void submit_gauge(const char *plugin_instance, const char *type,
93                          const char *type_instance, gauge_t value)
94
95 {
96   value_list_t vl = VALUE_LIST_INIT;
97
98   vl.values = &(value_t){.gauge = value};
99   vl.values_len = 1;
100   sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
101   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
102   sstrncpy(vl.type, type, sizeof(vl.type));
103   sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
104
105   plugin_dispatch_values(&vl);
106 } /* void submit_gauge */
107
108 static derive_t vserver_get_sock_bytes(const char *s) {
109   value_t v;
110   int status;
111
112   while (s[0] != '/')
113     ++s;
114
115   /* Remove '/' */
116   ++s;
117
118   status = parse_value(s, &v, DS_TYPE_DERIVE);
119   if (status != 0)
120     return -1;
121   return v.derive;
122 }
123
124 static int vserver_read(void) {
125   DIR *proc;
126
127   errno = 0;
128   proc = opendir(PROCDIR);
129   if (proc == NULL) {
130     ERROR("vserver plugin: fopen (%s): %s", PROCDIR, STRERRNO);
131     return -1;
132   }
133
134   while (42) {
135     struct dirent *dent;
136     int len;
137     char file[BUFSIZE];
138
139     FILE *fh;
140     char buffer[BUFSIZE];
141
142     struct stat statbuf;
143     char *cols[4];
144
145     int status;
146
147     errno = 0;
148     dent = readdir(proc);
149     if (dent == NULL) {
150       if (errno == 0) /* end of directory */
151         break;
152
153       ERROR("vserver plugin: failed to read directory %s: %s", PROCDIR,
154             STRERRNO);
155       closedir(proc);
156       return -1;
157     }
158
159     if (dent->d_name[0] == '.')
160       continue;
161
162     len = snprintf(file, sizeof(file), PROCDIR "/%s", dent->d_name);
163     if ((len < 0) || (len >= BUFSIZE))
164       continue;
165
166     status = stat(file, &statbuf);
167     if (status != 0) {
168       WARNING("vserver plugin: stat (%s) failed: %s", file, STRERRNO);
169       continue;
170     }
171
172     if (!S_ISDIR(statbuf.st_mode))
173       continue;
174
175     /* socket message accounting */
176     len = snprintf(file, sizeof(file), PROCDIR "/%s/cacct", dent->d_name);
177     if ((len < 0) || ((size_t)len >= sizeof(file)))
178       continue;
179
180     if (NULL == (fh = fopen(file, "r"))) {
181       ERROR("Cannot open '%s': %s", file, STRERRNO);
182     }
183
184     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
185       derive_t rx;
186       derive_t tx;
187       const char *type_instance;
188
189       if (strsplit(buffer, cols, 4) < 4)
190         continue;
191
192       if (0 == strcmp(cols[0], "UNIX:"))
193         type_instance = "unix";
194       else if (0 == strcmp(cols[0], "INET:"))
195         type_instance = "inet";
196       else if (0 == strcmp(cols[0], "INET6:"))
197         type_instance = "inet6";
198       else if (0 == strcmp(cols[0], "OTHER:"))
199         type_instance = "other";
200       else if (0 == strcmp(cols[0], "UNSPEC:"))
201         type_instance = "unspec";
202       else
203         continue;
204
205       rx = vserver_get_sock_bytes(cols[1]);
206       tx = vserver_get_sock_bytes(cols[2]);
207       /* cols[3] == errors */
208
209       traffic_submit(dent->d_name, type_instance, rx, tx);
210     } /* while (fgets) */
211
212     if (fh != NULL) {
213       fclose(fh);
214       fh = NULL;
215     }
216
217     /* thread information and load */
218     len = snprintf(file, sizeof(file), PROCDIR "/%s/cvirt", dent->d_name);
219     if ((len < 0) || ((size_t)len >= sizeof(file)))
220       continue;
221
222     if (NULL == (fh = fopen(file, "r"))) {
223       ERROR("Cannot open '%s': %s", file, STRERRNO);
224     }
225
226     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
227       int n = strsplit(buffer, cols, 4);
228
229       if (2 == n) {
230         const char *type_instance;
231         gauge_t value;
232
233         if (0 == strcmp(cols[0], "nr_threads:"))
234           type_instance = "total";
235         else if (0 == strcmp(cols[0], "nr_running:"))
236           type_instance = "running";
237         else if (0 == strcmp(cols[0], "nr_unintr:"))
238           type_instance = "uninterruptable";
239         else if (0 == strcmp(cols[0], "nr_onhold:"))
240           type_instance = "onhold";
241         else
242           continue;
243
244         value = atof(cols[1]);
245         submit_gauge(dent->d_name, "vs_threads", type_instance, value);
246       } else if (4 == n) {
247         if (0 == strcmp(cols[0], "loadavg:")) {
248           gauge_t snum = atof(cols[1]);
249           gauge_t mnum = atof(cols[2]);
250           gauge_t lnum = atof(cols[3]);
251           load_submit(dent->d_name, snum, mnum, lnum);
252         }
253       }
254     } /* while (fgets) */
255
256     if (fh != NULL) {
257       fclose(fh);
258       fh = NULL;
259     }
260
261     /* processes and memory usage */
262     len = snprintf(file, sizeof(file), PROCDIR "/%s/limit", dent->d_name);
263     if ((len < 0) || ((size_t)len >= sizeof(file)))
264       continue;
265
266     if (NULL == (fh = fopen(file, "r"))) {
267       ERROR("Cannot open '%s': %s", file, STRERRNO);
268     }
269
270     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
271       const char *type = "vs_memory";
272       const char *type_instance;
273       gauge_t value;
274
275       if (strsplit(buffer, cols, 2) < 2)
276         continue;
277
278       if (0 == strcmp(cols[0], "PROC:")) {
279         type = "vs_processes";
280         type_instance = "";
281         value = atof(cols[1]);
282       } else {
283         if (0 == strcmp(cols[0], "VM:"))
284           type_instance = "vm";
285         else if (0 == strcmp(cols[0], "VML:"))
286           type_instance = "vml";
287         else if (0 == strcmp(cols[0], "RSS:"))
288           type_instance = "rss";
289         else if (0 == strcmp(cols[0], "ANON:"))
290           type_instance = "anon";
291         else
292           continue;
293
294         value = atof(cols[1]) * pagesize;
295       }
296
297       submit_gauge(dent->d_name, type, type_instance, value);
298     } /* while (fgets) */
299
300     if (fh != NULL) {
301       fclose(fh);
302       fh = NULL;
303     }
304   } /* while (readdir) */
305
306   closedir(proc);
307
308   return 0;
309 } /* int vserver_read */
310
311 void module_register(void) {
312   plugin_register_init("vserver", vserver_init);
313   plugin_register_read("vserver", vserver_read);
314 } /* void module_register(void) */