Merge branch 'sb/iptables'
[collectd.git] / src / apache.c
1 /**
2  * collectd - src/apache.c
3  * Copyright (C) 2006  Florian octo Forster
4  * Copyright (C) 2007  Florent EppO Monbillard
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Authors:
21  *   Florian octo Forster <octo at verplant.org>
22  *   Florent EppO Monbillard <eppo at darox.net>
23  *   - connections/lighttpd extension
24  **/
25
26 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
29 #include "configfile.h"
30
31 #define MODULE_NAME "apache"
32
33 #if HAVE_LIBCURL && HAVE_CURL_CURL_H
34 #  define APACHE_HAVE_READ 1
35 #  include <curl/curl.h>
36 #else
37 #  define APACHE_HAVE_READ 0
38 #endif
39
40 static char *url    = NULL;
41 static char *user   = NULL;
42 static char *pass   = NULL;
43 static char *cacert = NULL;
44
45 #if HAVE_LIBCURL
46 static CURL *curl = NULL;
47
48 #define ABUFFER_SIZE 16384
49 static char apache_buffer[ABUFFER_SIZE];
50 static int  apache_buffer_len = 0;
51 static char apache_curl_error[CURL_ERROR_SIZE];
52 #endif /* HAVE_LIBCURL */
53
54 /* Limit to 2^27 bytes/s. That's what a gigabit-ethernet link can handle, in
55  * theory. */
56 static char *bytes_file = "apache/apache_bytes.rrd";
57 static char *bytes_ds_def[] =
58 {
59         "DS:count:COUNTER:"COLLECTD_HEARTBEAT":0:134217728",
60         NULL
61 };
62 static int bytes_ds_num = 1;
63
64 /* Limit to 2^20 requests/s */
65 static char *requests_file = "apache/apache_requests.rrd";
66 static char *requests_ds_def[] =
67 {
68         "DS:count:COUNTER:"COLLECTD_HEARTBEAT":0:1048576",
69         NULL
70 };
71 static int requests_ds_num = 1;
72
73 static char *scoreboard_file = "apache/apache_scoreboard-%s.rrd";
74 static char *scoreboard_ds_def[] =
75 {
76         "DS:count:GAUGE:"COLLECTD_HEARTBEAT":0:U",
77         NULL
78 };
79 static int scoreboard_ds_num = 1;
80
81 /* for lighttpd; Limit to 65536 active connections */
82 static char *connections_file = "apache/apache_connections.rrd";
83 static char *connections_ds_def[] =
84 {
85         "DS:connections:GAUGE:"COLLECTD_HEARTBEAT":0:65536", 
86         NULL
87 };
88 static int connections_ds_num = 1;
89
90 static char *config_keys[] =
91 {
92         "URL",
93         "User",
94         "Password",
95         "CACert",
96         NULL
97 };
98 static int config_keys_num = 4;
99
100 #if HAVE_LIBCURL
101 static size_t apache_curl_callback (void *buf, size_t size, size_t nmemb, void *stream)
102 {
103         size_t len = size * nmemb;
104
105         if ((apache_buffer_len + len) >= ABUFFER_SIZE)
106         {
107                 len = (ABUFFER_SIZE - 1) - apache_buffer_len;
108         }
109
110         if (len <= 0)
111                 return (len);
112
113         memcpy (apache_buffer + apache_buffer_len, (char *) buf, len);
114         apache_buffer_len += len;
115         apache_buffer[apache_buffer_len] = '\0';
116
117         return (len);
118 }
119 #endif /* HAVE_LIBCURL */
120
121 static int config_set (char **var, char *value)
122 {
123         if (*var != NULL)
124         {
125                 free (*var);
126                 *var = NULL;
127         }
128
129         if ((*var = strdup (value)) == NULL)
130                 return (1);
131         else
132                 return (0);
133 }
134
135 static int config (char *key, char *value)
136 {
137         if (strcasecmp (key, "url") == 0)
138                 return (config_set (&url, value));
139         else if (strcasecmp (key, "user") == 0)
140                 return (config_set (&user, value));
141         else if (strcasecmp (key, "password") == 0)
142                 return (config_set (&pass, value));
143         else if (strcasecmp (key, "cacert") == 0)
144                 return (config_set (&cacert, value));
145         else
146                 return (-1);
147 }
148
149 static void init (void)
150 {
151 #if HAVE_LIBCURL
152         static char credentials[1024];
153
154         if (curl != NULL)
155         {
156                 curl_easy_cleanup (curl);
157         }
158
159         if ((curl = curl_easy_init ()) == NULL)
160         {
161                 syslog (LOG_ERR, "apache: `curl_easy_init' failed.");
162                 return;
163         }
164
165         curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, apache_curl_callback);
166         curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
167         curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, apache_curl_error);
168
169         if (user != NULL)
170         {
171                 if (snprintf (credentials, 1024, "%s:%s", user, pass == NULL ? "" : pass) >= 1024)
172                 {
173                         syslog (LOG_ERR, "apache: Credentials would have been truncated.");
174                         return;
175                 }
176
177                 curl_easy_setopt (curl, CURLOPT_USERPWD, credentials);
178         }
179
180         if (url != NULL)
181         {
182                 curl_easy_setopt (curl, CURLOPT_URL, url);
183         }
184
185         if (cacert != NULL)
186         {
187                 curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
188         }
189 #endif /* HAVE_LIBCURL */
190 }
191
192 static void bytes_write (char *host, char *inst, char *val)
193 {
194         rrd_update_file (host, bytes_file, val, bytes_ds_def, bytes_ds_num);
195 }
196
197 static void requests_write (char *host, char *inst, char *val)
198 {
199         rrd_update_file (host, requests_file, val, requests_ds_def, requests_ds_num);
200 }
201
202 static void scoreboard_write (char *host, char *inst, char *val)
203 {
204         char buf[1024];
205
206         if (snprintf (buf, 1024, scoreboard_file, inst) >= 1024)
207                 return;
208
209         rrd_update_file (host, buf, val, scoreboard_ds_def, scoreboard_ds_num);
210 }
211
212 static void connections_write (char *host, char *inst, char *val)
213 {
214         rrd_update_file (host, connections_file, val, connections_ds_def,
215                         connections_ds_num);
216 }
217
218 #if APACHE_HAVE_READ
219 static void submit (char *type, char *inst, long long value)
220 {
221         char buf[1024];
222         int  status;
223
224         status = snprintf (buf, 1024, "%u:%lli", (unsigned int) curtime, value);
225         if (status < 0)
226         {
227                 syslog (LOG_ERR, "apache: bytes_submit: snprintf failed");
228                 return;
229         }
230         else if (status >= 1024)
231         {
232                 syslog (LOG_WARNING, "apache: bytes_submit: snprintf was truncated");
233                 return;
234         }
235
236         plugin_submit (type, inst, buf);
237 }
238
239 static void submit_scoreboard (char *buf)
240 {
241         /*
242          * Scoreboard Key:
243          * "_" Waiting for Connection, "S" Starting up, "R" Reading Request,
244          * "W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup,
245          * "C" Closing connection, "L" Logging, "G" Gracefully finishing,
246          * "I" Idle cleanup of worker, "." Open slot with no current process
247          */
248         long long open      = 0LL;
249         long long waiting   = 0LL;
250         long long starting  = 0LL;
251         long long reading   = 0LL;
252         long long sending   = 0LL;
253         long long keepalive = 0LL;
254         long long dnslookup = 0LL;
255         long long closing   = 0LL;
256         long long logging   = 0LL;
257         long long finishing = 0LL;
258         long long idle_cleanup = 0LL;
259
260         int i;
261
262         for (i = 0; buf[i] != '\0'; i++)
263         {
264                 if (buf[i] == '.') open++;
265                 else if (buf[i] == '_') waiting++;
266                 else if (buf[i] == 'S') starting++;
267                 else if (buf[i] == 'R') reading++;
268                 else if (buf[i] == 'W') sending++;
269                 else if (buf[i] == 'K') keepalive++;
270                 else if (buf[i] == 'D') dnslookup++;
271                 else if (buf[i] == 'C') closing++;
272                 else if (buf[i] == 'L') logging++;
273                 else if (buf[i] == 'G') finishing++;
274                 else if (buf[i] == 'I') idle_cleanup++;
275         }
276
277         submit ("apache_scoreboard", "open"     , open);
278         submit ("apache_scoreboard", "waiting"  , waiting);
279         submit ("apache_scoreboard", "starting" , starting);
280         submit ("apache_scoreboard", "reading"  , reading);
281         submit ("apache_scoreboard", "sending"  , sending);
282         submit ("apache_scoreboard", "keepalive", keepalive);
283         submit ("apache_scoreboard", "dnslookup", dnslookup);
284         submit ("apache_scoreboard", "closing"  , closing);
285         submit ("apache_scoreboard", "logging"  , logging);
286         submit ("apache_scoreboard", "finishing", finishing);
287         submit ("apache_scoreboard", "idle_cleanup", idle_cleanup);
288 }
289
290 static void apache_read (void)
291 {
292         int i;
293
294         char *ptr;
295         char *lines[16];
296         int   lines_num = 0;
297
298         char *fields[4];
299         int   fields_num;
300
301         if (curl == NULL)
302                 return;
303         if (url == NULL)
304                 return;
305
306         apache_buffer_len = 0;
307         if (curl_easy_perform (curl) != 0)
308         {
309                 syslog (LOG_WARNING, "apache: curl_easy_perform failed: %s", apache_curl_error);
310                 return;
311         }
312
313         ptr = apache_buffer;
314         while ((lines[lines_num] = strtok (ptr, "\n\r")) != NULL)
315         {
316                 ptr = NULL;
317                 lines_num++;
318
319                 if (lines_num >= 16)
320                         break;
321         }
322
323         for (i = 0; i < lines_num; i++)
324         {
325                 fields_num = strsplit (lines[i], fields, 4);
326
327                 if (fields_num == 3)
328                 {
329                         if ((strcmp (fields[0], "Total") == 0)
330                                         && (strcmp (fields[1], "Accesses:") == 0))
331                                 submit ("apache_requests", NULL, atoll (fields[2]));
332                         else if ((strcmp (fields[0], "Total") == 0)
333                                         && (strcmp (fields[1], "kBytes:") == 0))
334                                 submit ("apache_bytes", NULL, 1024LL * atoll (fields[2]));
335                 }
336                 else if (fields_num == 2)
337                 {
338                         if (strcmp (fields[0], "Scoreboard:") == 0)
339                                 submit_scoreboard (fields[1]);
340                         else if (strcmp (fields[0], "BusyServers:") == 0)
341                                 submit ("apache_connections", NULL, atol (fields[1]));
342                 }
343         }
344
345         apache_buffer_len = 0;
346 }
347 #else
348 #  define apache_read NULL
349 #endif /* APACHE_HAVE_READ */
350
351 void module_register (void)
352 {
353         plugin_register (MODULE_NAME, init, apache_read, NULL);
354         plugin_register ("apache_requests",   NULL, NULL, requests_write);
355         plugin_register ("apache_bytes",      NULL, NULL, bytes_write);
356         plugin_register ("apache_scoreboard", NULL, NULL, scoreboard_write);
357         plugin_register ("apache_connections", NULL, NULL, connections_write);
358         cf_register (MODULE_NAME, config, config_keys, config_keys_num);
359 }
360
361 #undef MODULE_NAME