Annotation of embedaddon/lighttpd/src/mod_rrdtool.c, revision 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>