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