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