Annotation of embedaddon/lighttpd/src/mod_rrdtool.c, revision 1.1.1.2
1.1 misho 1: #include "server.h"
2: #include "connections.h"
3: #include "response.h"
4: #include "connections.h"
5: #include "log.h"
6:
7: #include "plugin.h"
8: #include <sys/types.h>
9:
1.1.1.2 ! misho 10: #include <assert.h>
1.1 misho 11: #include <fcntl.h>
12: #include <stdlib.h>
13: #include <stdio.h>
14: #include <string.h>
15: #include <unistd.h>
16: #include <errno.h>
17: #include <time.h>
18:
19: #ifdef HAVE_FORK
20: /* no need for waitpid if we don't have fork */
21: #include <sys/wait.h>
22: #endif
23: typedef struct {
24: buffer *path_rrdtool_bin;
25: buffer *path_rrd;
26:
27: double requests, *requests_ptr;
28: double bytes_written, *bytes_written_ptr;
29: double bytes_read, *bytes_read_ptr;
30: } plugin_config;
31:
32: typedef struct {
33: PLUGIN_DATA;
34:
35: buffer *cmd;
36: buffer *resp;
37:
38: int read_fd, write_fd;
39: pid_t rrdtool_pid;
40:
41: int rrdtool_running;
42:
43: plugin_config **config_storage;
44: plugin_config conf;
45: } plugin_data;
46:
47: INIT_FUNC(mod_rrd_init) {
48: plugin_data *p;
49:
50: p = calloc(1, sizeof(*p));
51:
52: p->resp = buffer_init();
53: p->cmd = buffer_init();
54:
55: return p;
56: }
57:
58: FREE_FUNC(mod_rrd_free) {
59: plugin_data *p = p_d;
60: size_t i;
61:
62: if (!p) return HANDLER_GO_ON;
63:
64: if (p->config_storage) {
65: for (i = 0; i < srv->config_context->used; i++) {
66: plugin_config *s = p->config_storage[i];
67:
68: buffer_free(s->path_rrdtool_bin);
69: buffer_free(s->path_rrd);
70:
71: free(s);
72: }
73: }
74: buffer_free(p->cmd);
75: buffer_free(p->resp);
76:
77: free(p->config_storage);
78:
79: if (p->rrdtool_pid) {
80: int status;
81: close(p->read_fd);
82: close(p->write_fd);
83: #ifdef HAVE_FORK
84: /* collect status */
85: waitpid(p->rrdtool_pid, &status, 0);
86: #endif
87: }
88:
89: free(p);
90:
91: return HANDLER_GO_ON;
92: }
93:
94: static int mod_rrd_create_pipe(server *srv, plugin_data *p) {
95: #ifdef HAVE_FORK
96: pid_t pid;
97:
98: int to_rrdtool_fds[2];
99: int from_rrdtool_fds[2];
100: if (pipe(to_rrdtool_fds)) {
101: log_error_write(srv, __FILE__, __LINE__, "ss",
102: "pipe failed: ", strerror(errno));
103: return -1;
104: }
105:
106: if (pipe(from_rrdtool_fds)) {
107: log_error_write(srv, __FILE__, __LINE__, "ss",
108: "pipe failed: ", strerror(errno));
109: return -1;
110: }
111:
112: /* fork, execve */
113: switch (pid = fork()) {
114: case 0: {
115: /* child */
116: char **args;
117: int argc;
118: int i = 0;
119: char *dash = "-";
120:
121: /* move stdout to from_rrdtool_fd[1] */
122: close(STDOUT_FILENO);
123: dup2(from_rrdtool_fds[1], STDOUT_FILENO);
124: close(from_rrdtool_fds[1]);
125: /* not needed */
126: close(from_rrdtool_fds[0]);
127:
128: /* move the stdin to to_rrdtool_fd[0] */
129: close(STDIN_FILENO);
130: dup2(to_rrdtool_fds[0], STDIN_FILENO);
131: close(to_rrdtool_fds[0]);
132: /* not needed */
133: close(to_rrdtool_fds[1]);
134:
135: /* set up args */
136: argc = 3;
137: args = malloc(sizeof(*args) * argc);
138: i = 0;
139:
140: args[i++] = p->conf.path_rrdtool_bin->ptr;
141: args[i++] = dash;
142: args[i ] = NULL;
143:
144: /* we don't need the client socket */
145: for (i = 3; i < 256; i++) {
146: close(i);
147: }
148:
149: /* exec the cgi */
150: execv(args[0], args);
151:
152: /* log_error_write(srv, __FILE__, __LINE__, "sss", "spawing rrdtool failed: ", strerror(errno), args[0]); */
153:
154: /* */
155: SEGFAULT();
156: break;
157: }
158: case -1:
159: /* error */
160: log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno));
161: break;
162: default: {
163: /* father */
164:
165: close(from_rrdtool_fds[1]);
166: close(to_rrdtool_fds[0]);
167:
168: /* register PID and wait for them asyncronously */
169: p->write_fd = to_rrdtool_fds[1];
170: p->read_fd = from_rrdtool_fds[0];
171: p->rrdtool_pid = pid;
172:
1.1.1.2 ! misho 173: fd_close_on_exec(p->write_fd);
! 174: fd_close_on_exec(p->read_fd);
1.1 misho 175:
176: break;
177: }
178: }
179:
180: return 0;
181: #else
182: return -1;
183: #endif
184: }
185:
186: /* read/write wrappers to catch EINTR */
187:
188: /* write to blocking socket; blocks until all data is sent, write returns 0 or an error (apart from EINTR) occurs. */
189: static ssize_t safe_write(int fd, const void *buf, size_t count) {
190: ssize_t res, sum = 0;
191:
192: for (;;) {
193: res = write(fd, buf, count);
194: if (res >= 0) {
195: sum += res;
196: /* do not try again if res == 0 */
197: if (res == 0 || (size_t) res == count) return sum;
198: count -= res;
199: buf = (const char*) buf + res;
200: continue;
201: }
202: switch (errno) {
203: case EINTR:
204: continue;
205: default:
206: return -1;
207: }
208: }
209: }
210:
211: /* this assumes we get enough data on a successful read */
212: static ssize_t safe_read(int fd, void *buf, size_t count) {
213: ssize_t res;
214:
215: for (;;) {
216: res = read(fd, buf, count);
217: if (res >= 0) return res;
218: switch (errno) {
219: case EINTR:
220: continue;
221: default:
222: return -1;
223: }
224: }
225: }
226:
227: static int mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) {
228: struct stat st;
229: int r;
230:
231: /* check if DB already exists */
232: if (0 == stat(s->path_rrd->ptr, &st)) {
233: /* check if it is plain file */
234: if (!S_ISREG(st.st_mode)) {
235: log_error_write(srv, __FILE__, __LINE__, "sb",
236: "not a regular file:", s->path_rrd);
237: return HANDLER_ERROR;
238: }
239:
240: /* still create DB if it's empty file */
241: if (st.st_size > 0) {
242: return HANDLER_GO_ON;
243: }
244: }
245:
246: /* create a new one */
247: buffer_copy_string_len(p->cmd, CONST_STR_LEN("create "));
248: buffer_append_string_buffer(p->cmd, s->path_rrd);
249: buffer_append_string_len(p->cmd, CONST_STR_LEN(
250: " --step 60 "
251: "DS:InOctets:ABSOLUTE:600:U:U "
252: "DS:OutOctets:ABSOLUTE:600:U:U "
253: "DS:Requests:ABSOLUTE:600:U:U "
254: "RRA:AVERAGE:0.5:1:600 "
255: "RRA:AVERAGE:0.5:6:700 "
256: "RRA:AVERAGE:0.5:24:775 "
257: "RRA:AVERAGE:0.5:288:797 "
258: "RRA:MAX:0.5:1:600 "
259: "RRA:MAX:0.5:6:700 "
260: "RRA:MAX:0.5:24:775 "
261: "RRA:MAX:0.5:288:797 "
262: "RRA:MIN:0.5:1:600 "
263: "RRA:MIN:0.5:6:700 "
264: "RRA:MIN:0.5:24:775 "
265: "RRA:MIN:0.5:288:797\n"));
266:
267: if (-1 == (safe_write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) {
268: log_error_write(srv, __FILE__, __LINE__, "ss",
269: "rrdtool-write: failed", strerror(errno));
270:
271: return HANDLER_ERROR;
272: }
273:
274: buffer_prepare_copy(p->resp, 4096);
275: if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size))) {
276: log_error_write(srv, __FILE__, __LINE__, "ss",
277: "rrdtool-read: failed", strerror(errno));
278:
279: return HANDLER_ERROR;
280: }
281:
282: p->resp->used = r;
283:
284: if (p->resp->ptr[0] != 'O' ||
285: p->resp->ptr[1] != 'K') {
286: log_error_write(srv, __FILE__, __LINE__, "sbb",
287: "rrdtool-response:", p->cmd, p->resp);
288:
289: return HANDLER_ERROR;
290: }
291:
292: return HANDLER_GO_ON;
293: }
294:
295: #define PATCH(x) \
296: p->conf.x = s->x;
297: static int mod_rrd_patch_connection(server *srv, connection *con, plugin_data *p) {
298: size_t i, j;
299: plugin_config *s = p->config_storage[0];
300:
301: PATCH(path_rrdtool_bin);
302: PATCH(path_rrd);
303:
304: p->conf.bytes_written_ptr = &(s->bytes_written);
305: p->conf.bytes_read_ptr = &(s->bytes_read);
306: p->conf.requests_ptr = &(s->requests);
307:
308: /* skip the first, the global context */
309: for (i = 1; i < srv->config_context->used; i++) {
310: data_config *dc = (data_config *)srv->config_context->data[i];
311: s = p->config_storage[i];
312:
313: /* condition didn't match */
314: if (!config_check_cond(srv, con, dc)) continue;
315:
316: /* merge config */
317: for (j = 0; j < dc->value->used; j++) {
318: data_unset *du = dc->value->data[j];
319:
320: if (buffer_is_equal_string(du->key, CONST_STR_LEN("rrdtool.db-name"))) {
321: PATCH(path_rrd);
322: /* get pointers to double values */
323:
324: p->conf.bytes_written_ptr = &(s->bytes_written);
325: p->conf.bytes_read_ptr = &(s->bytes_read);
326: p->conf.requests_ptr = &(s->requests);
327: }
328: }
329: }
330:
331: return 0;
332: }
333: #undef PATCH
334:
335: SETDEFAULTS_FUNC(mod_rrd_set_defaults) {
336: plugin_data *p = p_d;
337: size_t i;
338:
339: config_values_t cv[] = {
340: { "rrdtool.binary", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
341: { "rrdtool.db-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
342: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
343: };
344:
345: if (!p) return HANDLER_ERROR;
346:
1.1.1.2 ! misho 347: force_assert(srv->config_context->used > 0);
! 348: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1 misho 349:
350: for (i = 0; i < srv->config_context->used; i++) {
351: plugin_config *s;
352:
353: s = calloc(1, sizeof(plugin_config));
354: s->path_rrdtool_bin = buffer_init();
355: s->path_rrd = buffer_init();
356: s->requests = 0;
357: s->bytes_written = 0;
358: s->bytes_read = 0;
359:
360: cv[0].destination = s->path_rrdtool_bin;
361: cv[1].destination = s->path_rrd;
362:
363: p->config_storage[i] = s;
364:
365: if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
366: return HANDLER_ERROR;
367: }
368:
369: if (i > 0 && !buffer_is_empty(s->path_rrdtool_bin)) {
370: /* path_rrdtool_bin is a global option */
371:
372: log_error_write(srv, __FILE__, __LINE__, "s",
373: "rrdtool.binary can only be set as a global option.");
374:
375: return HANDLER_ERROR;
376: }
377:
378: }
379:
380: p->conf.path_rrdtool_bin = p->config_storage[0]->path_rrdtool_bin;
381: p->rrdtool_running = 0;
382:
383: /* check for dir */
384:
385: if (buffer_is_empty(p->conf.path_rrdtool_bin)) {
386: log_error_write(srv, __FILE__, __LINE__, "s",
387: "rrdtool.binary has to be set");
388: return HANDLER_ERROR;
389: }
390:
391: /* open the pipe to rrdtool */
392: if (mod_rrd_create_pipe(srv, p)) {
393: return HANDLER_ERROR;
394: }
395:
396: p->rrdtool_running = 1;
397:
398: return HANDLER_GO_ON;
399: }
400:
401: TRIGGER_FUNC(mod_rrd_trigger) {
402: plugin_data *p = p_d;
403: size_t i;
404:
405: if (!p->rrdtool_running) return HANDLER_GO_ON;
406: if ((srv->cur_ts % 60) != 0) return HANDLER_GO_ON;
407:
408: for (i = 0; i < srv->config_context->used; i++) {
409: plugin_config *s = p->config_storage[i];
410: int r;
411:
412: if (buffer_is_empty(s->path_rrd)) continue;
413:
414: /* write the data down every minute */
415:
416: if (HANDLER_GO_ON != mod_rrdtool_create_rrd(srv, p, s)) return HANDLER_ERROR;
417:
418: buffer_copy_string_len(p->cmd, CONST_STR_LEN("update "));
419: buffer_append_string_buffer(p->cmd, s->path_rrd);
420: buffer_append_string_len(p->cmd, CONST_STR_LEN(" N:"));
421: buffer_append_off_t(p->cmd, s->bytes_read);
422: buffer_append_string_len(p->cmd, CONST_STR_LEN(":"));
423: buffer_append_off_t(p->cmd, s->bytes_written);
424: buffer_append_string_len(p->cmd, CONST_STR_LEN(":"));
425: buffer_append_long(p->cmd, s->requests);
426: buffer_append_string_len(p->cmd, CONST_STR_LEN("\n"));
427:
428: if (-1 == (r = safe_write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) {
429: p->rrdtool_running = 0;
430:
431: log_error_write(srv, __FILE__, __LINE__, "ss",
432: "rrdtool-write: failed", strerror(errno));
433:
434: return HANDLER_ERROR;
435: }
436:
437: buffer_prepare_copy(p->resp, 4096);
1.1.1.2 ! misho 438: if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size - 1))) {
1.1 misho 439: p->rrdtool_running = 0;
440:
441: log_error_write(srv, __FILE__, __LINE__, "ss",
442: "rrdtool-read: failed", strerror(errno));
443:
444: return HANDLER_ERROR;
445: }
446:
1.1.1.2 ! misho 447: p->resp->used = r + 1;
! 448: p->resp->ptr[r] = '\0';
1.1 misho 449:
450: if (p->resp->ptr[0] != 'O' ||
451: p->resp->ptr[1] != 'K') {
452: /* don't fail on this error if we just started (graceful restart, the old one might have just updated too) */
453: if (!(strstr(p->resp->ptr, "(minimum one second step)") && (srv->cur_ts - srv->startup_ts < 3))) {
454: p->rrdtool_running = 0;
455:
456: log_error_write(srv, __FILE__, __LINE__, "sbb",
457: "rrdtool-response:", p->cmd, p->resp);
458:
459: return HANDLER_ERROR;
460: }
461: }
462: s->requests = 0;
463: s->bytes_written = 0;
464: s->bytes_read = 0;
465: }
466:
467: return HANDLER_GO_ON;
468: }
469:
470: REQUESTDONE_FUNC(mod_rrd_account) {
471: plugin_data *p = p_d;
472:
473: mod_rrd_patch_connection(srv, con, p);
474:
475: *(p->conf.requests_ptr) += 1;
476: *(p->conf.bytes_written_ptr) += con->bytes_written;
477: *(p->conf.bytes_read_ptr) += con->bytes_read;
478:
479: return HANDLER_GO_ON;
480: }
481:
482: int mod_rrdtool_plugin_init(plugin *p);
483: int mod_rrdtool_plugin_init(plugin *p) {
484: p->version = LIGHTTPD_VERSION_ID;
485: p->name = buffer_init_string("rrd");
486:
487: p->init = mod_rrd_init;
488: p->cleanup = mod_rrd_free;
489: p->set_defaults= mod_rrd_set_defaults;
490:
491: p->handle_trigger = mod_rrd_trigger;
492: p->handle_request_done = mod_rrd_account;
493:
494: p->data = NULL;
495:
496: return 0;
497: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>