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